changelog shortlog graph tags branches files raw help

Mercurial > demo / changeset: bugfixes, tweaks to run.lisp

changeset 31: 77da08c7f445
parent 30: aa37feddcfb2
child 32: 02aa015bff73
author: ellis <ellis@rwest.io>
date: Sun, 18 Jun 2023 22:25:28 -0400
files: default-config.sexp demo.asd makefile readme.org run.lisp src/package.lisp system-index.txt tools/asdf.lisp tools/prepare-image.lisp vendor/system-index.txt
description: bugfixes, tweaks to run.lisp
     1.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2+++ b/default-config.sexp	Sun Jun 18 22:25:28 2023 -0400
     1.3@@ -0,0 +1,1 @@
     1.4+;; demo user configuration file
     1.5\ No newline at end of file
     2.1--- a/demo.asd	Thu Jun 15 22:01:40 2023 -0400
     2.2+++ b/demo.asd	Sun Jun 18 22:25:28 2023 -0400
     2.3@@ -1,9 +1,4 @@
     2.4 ;;; demo.asd
     2.5-(in-package #:asdf-user)
     2.6-
     2.7-(defsystem "demo/sys"
     2.8-  :components ((:file "src/package")))
     2.9-
    2.10 (defsystem "demo"
    2.11   :version "0.1.0"
    2.12   :author "ellis <ellis@rwest.io>"
    2.13@@ -13,9 +8,12 @@
    2.14   :bug-tracker "https://lab.rwest.io/otom8/demo/issues"
    2.15   :source-control (:hg "https://lab.rwest.io/otom8/demo")
    2.16   :license "WTFPL"
    2.17-  :depends-on ("demo/sys" :cl-dbi :sxql :log4cl :verbose :bordeaux-threads :clingon :clog)
    2.18+  :depends-on (:log4cl :bordeaux-threads :clingon :clog)
    2.19   :in-order-to ((test-op (test-op "src/test")))
    2.20-  :build-pathname "demo")
    2.21+  :build-pathname "demo"
    2.22+  :components ((:module "src"
    2.23+		:components ((:file "package")
    2.24+			     (:file "cfg")))))
    2.25 
    2.26 (defmethod perform :after ((op load-op) (c (eql (find-system :demo))))
    2.27   (pushnew :demo *features*))
     3.1--- a/makefile	Thu Jun 15 22:01:40 2023 -0400
     3.2+++ b/makefile	Sun Jun 18 22:25:28 2023 -0400
     3.3@@ -1,3 +1,4 @@
     3.4+# otom8/demo makefile
     3.5 MODE?=release
     3.6 LISP?=sbcl
     3.7 CFG?=default.cfg
     3.8@@ -10,7 +11,7 @@
     3.9 RS:Cargo.toml rustfmt.toml src/crates/*
    3.10 CL:*/*.asd */*.lisp
    3.11 deps:;
    3.12-clean:;rm -rf *.fasl;cargo clean
    3.13+clean:;rm -rf */*.fasl;cargo clean
    3.14 fmt:$(RS);cargo fmt
    3.15 build:$(RS) $(CL);cargo build --$(MODE);$(L_D)
    3.16 	--eval '(asdf:make "demo")' \
     4.1--- a/readme.org	Thu Jun 15 22:01:40 2023 -0400
     4.2+++ b/readme.org	Sun Jun 18 22:25:28 2023 -0400
     4.3@@ -24,20 +24,12 @@
     4.4   #+begin_src bash
     4.5     ./tools/deps.sh
     4.6   #+end_src
     4.7-  - Rust =curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh=
     4.8-  - Common Lisp
     4.9-    - on Linux ::
    4.10-      - Ubuntu/Debian :: =sudo apt-get install sbcl=
    4.11-      - Arch BTW :: =sudo pacman -S sbcl=
    4.12-    - on MacOS :: =brew install sbcl=
    4.13-    - on Windows :: download from
    4.14-      <https://www.sbcl.org/platform-table.html> and figure it out.
    4.15 - *make executables* \\
    4.16   Simply run =make build=. Read the ~makefile~ and change the options
    4.17   as needed.
    4.18-- M :: Mode (debug, release)
    4.19-- L :: Lisp (sbcl, cmucl, ccl)
    4.20-- C :: Config (default.cfg)
    4.21+- MODE :: Mode (debug, release)
    4.22+- LISP :: Lisp (sbcl, cmucl, ccl)
    4.23+- CFG :: Config (default.cfg)
    4.24 ** Run
    4.25 #+begin_src shell
    4.26   ./demo -i
     5.1--- a/run.lisp	Thu Jun 15 22:01:40 2023 -0400
     5.2+++ b/run.lisp	Sun Jun 18 22:25:28 2023 -0400
     5.3@@ -1,2 +1,2 @@
     5.4-(load "tools/prepare-image")
     5.5-(load "tools/init")
     5.6+(defparameter *cwd* (asdf:system-source-directory :demo))
     5.7+(load (merge-pathnames "tools/build-image.lisp" *cwd*))
     6.1--- a/src/package.lisp	Thu Jun 15 22:01:40 2023 -0400
     6.2+++ b/src/package.lisp	Sun Jun 18 22:25:28 2023 -0400
     6.3@@ -1,9 +1,7 @@
     6.4 ;; demo packages.lisp
     6.5-(defpackage :demo-sys
     6.6-  (:nicknames :ds))
     6.7+(defpackage :demo-sys)
     6.8 (defpackage :demo-utils
     6.9   (:use :demo-sys)
    6.10-  (:nicknames :dutils)
    6.11   (:export
    6.12    #:source-dir
    6.13    #:random-id
    6.14@@ -19,13 +17,11 @@
    6.15   (:nicknames :ddb))
    6.16 (defpackage :demo-ui
    6.17   (:use :demo-sys)
    6.18-  (:nicknames :dui)
    6.19   (:export
    6.20    #:on-new-window
    6.21    #:start-ui))
    6.22 (defpackage :demo-cli
    6.23   (:use :demo-sys)
    6.24-  (:nicknames :dcli)
    6.25   (:export
    6.26    #:run-cli
    6.27    #:demo-path
    6.28@@ -35,9 +31,7 @@
    6.29    #:cli-cmd))
    6.30 (defpackage :demo
    6.31   (:use #:cl #:demo-sys #:demo-utils #:demo-db #:demo-ui #:demo-cli)
    6.32-  (:nicknames :d)
    6.33   (:local-nicknames
    6.34-   (#:v #:org.shirakumo.verbose)
    6.35    (#:bt #:bordeaux-threads)
    6.36    (#:cli #:clingon)))
    6.37 (defpackage :demo-user
     7.1--- a/system-index.txt	Thu Jun 15 22:01:40 2023 -0400
     7.2+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.3@@ -1,1 +0,0 @@
     7.4-demo.asd
     8.1--- a/tools/asdf.lisp	Thu Jun 15 22:01:40 2023 -0400
     8.2+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.3@@ -1,13987 +0,0 @@
     8.4-;;; -*- mode: Lisp; Base: 10 ; Syntax: ANSI-Common-Lisp ; Package: CL-USER ; buffer-read-only: t; -*-
     8.5-;;; This is ASDF 3.3.6: Another System Definition Facility.
     8.6-;;;
     8.7-;;; Feedback, bug reports, and patches are all welcome:
     8.8-;;; please mail to <asdf-devel@common-lisp.net>.
     8.9-;;; Note first that the canonical source for ASDF is presently
    8.10-;;; <URL:http://common-lisp.net/project/asdf/>.
    8.11-;;;
    8.12-;;; If you obtained this copy from anywhere else, and you experience
    8.13-;;; trouble using it, or find bugs, you may want to check at the
    8.14-;;; location above for a more recent version (and for documentation
    8.15-;;; and test files, if your copy came without them) before reporting
    8.16-;;; bugs.  There are usually two "supported" revisions - the git master
    8.17-;;; branch is the latest development version, whereas the git release
    8.18-;;; branch may be slightly older but is considered `stable'
    8.19-
    8.20-;;; -- LICENSE START
    8.21-;;; (This is the MIT / X Consortium license as taken from
    8.22-;;;  http://www.opensource.org/licenses/mit-license.html on or about
    8.23-;;;  Monday; July 13, 2009)
    8.24-;;;
    8.25-;;; Copyright (c) 2001-2019 Daniel Barlow and contributors
    8.26-;;;
    8.27-;;; Permission is hereby granted, free of charge, to any person obtaining
    8.28-;;; a copy of this software and associated documentation files (the
    8.29-;;; "Software"), to deal in the Software without restriction, including
    8.30-;;; without limitation the rights to use, copy, modify, merge, publish,
    8.31-;;; distribute, sublicense, and/or sell copies of the Software, and to
    8.32-;;; permit persons to whom the Software is furnished to do so, subject to
    8.33-;;; the following conditions:
    8.34-;;;
    8.35-;;; The above copyright notice and this permission notice shall be
    8.36-;;; included in all copies or substantial portions of the Software.
    8.37-;;;
    8.38-;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    8.39-;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    8.40-;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    8.41-;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
    8.42-;;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    8.43-;;; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
    8.44-;;; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    8.45-;;;
    8.46-;;; -- LICENSE END
    8.47-
    8.48-;;; The problem with writing a defsystem replacement is bootstrapping:
    8.49-;;; we can't use defsystem to compile it.  Hence, all in one file.
    8.50-
    8.51-#+genera
    8.52-(eval-when (:compile-toplevel :load-toplevel :execute)
    8.53-  (multiple-value-bind (system-major system-minor)
    8.54-      (sct:get-system-version)
    8.55-    (multiple-value-bind (is-major is-minor)
    8.56-	(sct:get-system-version "Intel-Support")
    8.57-      (unless (or (> system-major 452)
    8.58-		  (and is-major
    8.59-		       (or (> is-major 3)
    8.60-			   (and (= is-major 3) (> is-minor 86)))))
    8.61-	(error "ASDF requires either System 453 or later or Intel Support 3.87 or later")))))
    8.62-;;;; ---------------------------------------------------------------------------
    8.63-;;;; ASDF package upgrade, including implementation-dependent magic.
    8.64-;;
    8.65-;; See https://bugs.launchpad.net/asdf/+bug/485687
    8.66-;;
    8.67-
    8.68-;; CAUTION: The definition of the UIOP/PACKAGE package MUST NOT CHANGE,
    8.69-;; NOT NOW, NOT EVER, NOT UNDER ANY CIRCUMSTANCE. NEVER.
    8.70-;; ... and the same goes for UIOP/PACKAGE-LOCAL-NICKNAMES.
    8.71-;;
    8.72-;; The entire point of UIOP/PACKAGE is to address the fact that the CL standard
    8.73-;; *leaves it unspecified what happens when a package is redefined incompatibly*.
    8.74-;; For instance, SBCL 1.4.2 will signal a full WARNING when this happens,
    8.75-;; throwing a wrench in upgrading code with ASDF itself, while continuing to
    8.76-;; export old symbols it now shouldn't as it also exports new ones,
    8.77-;; causing problems with code that relies on the new/current exports.
    8.78-;; CLISP and CCL also exports both sets of symbols, though without any WARNING.
    8.79-;; ABCL 1.6.1 will plainly ignore the new definition.
    8.80-;; Other implementations may do whatever they want and change their behavior at any time.
    8.81-;; ***Using DEFPACKAGE twice with different definitions is nasal-demon territory.***
    8.82-;;
    8.83-;; Thus we define UIOP/PACKAGE:DEFINE-PACKAGE with which packages can be defined
    8.84-;; in an upgrade-friendly way: the new definition is authoritative, and
    8.85-;; the package will define and export exactly those symbols in the new definition,
    8.86-;; no more and no fewer, whereas it is well-defined what happens to previous symbols.
    8.87-;; However, for obvious bootstrap reasons, we cannot use DEFINE-PACKAGE
    8.88-;; to define UIOP/PACKAGE itself, only DEFPACKAGE.
    8.89-;; Therefore, unlike the other packages in ASDF, UIOP/PACKAGE is immutable,
    8.90-;; now and forever. It is frozen for the aeons to come, like the CL package itself,
    8.91-;; to the same exact state it was defined at its inception, in ASDF 2.27 in 2013.
    8.92-;; The same goes for UIOP/PACKAGE-LOCAL-NICKNAMES, that we use internally.
    8.93-;;
    8.94-;; If you ever must define new symbols in this file, you can and must
    8.95-;; export them from a different package, possibly defined in the same file,
    8.96-;; say a package UIOP/PACKAGE* defined at the end of this file with DEFINE-PACKAGE,
    8.97-;; that might use :import-from to import the symbols from UIOP/PACKAGE,
    8.98-;; if you must somehow define them in UIOP/PACKAGE.
    8.99-
   8.100-(defpackage :uiop/package ;;; THOU SHALT NOT modify this definition, EVER. See explanations above.
   8.101-  (:use :common-lisp)
   8.102-  (:export
   8.103-   #:find-package* #:find-symbol* #:symbol-call
   8.104-   #:intern* #:export* #:import* #:shadowing-import* #:shadow* #:make-symbol* #:unintern*
   8.105-   #:symbol-shadowing-p #:home-package-p
   8.106-   #:symbol-package-name #:standard-common-lisp-symbol-p
   8.107-   #:reify-package #:unreify-package #:reify-symbol #:unreify-symbol
   8.108-   #:nuke-symbol-in-package #:nuke-symbol #:rehome-symbol
   8.109-   #:ensure-package-unused #:delete-package*
   8.110-   #:package-names #:packages-from-names #:fresh-package-name #:rename-package-away
   8.111-   #:package-definition-form #:parse-define-package-form
   8.112-   #:ensure-package #:define-package
   8.113-   ))
   8.114-
   8.115-(in-package :uiop/package)
   8.116-
   8.117-;;; package local nicknames feature.
   8.118-;;; This can't be deferred until common-lisp.lisp, where most such features are set.
   8.119-;;; ABCL and CCL already define this feature appropriately.
   8.120-;;; Seems to be unconditionally present for SBCL, ACL, and CLASP
   8.121-;;; Don't know about ECL, or others
   8.122-(eval-when (:load-toplevel :compile-toplevel :execute)
   8.123-  ;; ABCL pushes :package-local-nicknames without UIOP interfering,
   8.124-  ;; and Lispworks will do so
   8.125-  #+(or sbcl clasp)
   8.126-  (pushnew :package-local-nicknames *features*)
   8.127-  #+allegro
   8.128-  (let ((fname (find-symbol (symbol-name '#:add-package-local-nickname) '#:excl)))
   8.129-    (when (and fname (fboundp fname))
   8.130-      (pushnew :package-local-nicknames *features*))))
   8.131-
   8.132-;;; THOU SHALT NOT modify this definition, EVER, *EXCEPT* to add a new implementation.
   8.133-;; If you somehow need to modify the API in any way,
   8.134-;; you will need to create another, differently named, and just as immutable package.
   8.135-#+package-local-nicknames
   8.136-(defpackage :uiop/package-local-nicknames
   8.137-  (:use :cl)
   8.138-  (:import-from
   8.139-   #+allegro #:excl
   8.140-   #+sbcl #:sb-ext
   8.141-   #+(or clasp abcl ecl) #:ext
   8.142-   #+ccl #:ccl
   8.143-   #+lispworks #:hcl
   8.144-   #-(or allegro sbcl clasp abcl ccl lispworks ecl)
   8.145-   (error "Don't know from which package this lisp supplies the local-package-nicknames API.")
   8.146-   #:remove-package-local-nickname #:package-local-nicknames #:add-package-local-nickname)
   8.147-  (:export
   8.148-   #:add-package-local-nickname #:remove-package-local-nickname #:package-local-nicknames))
   8.149-
   8.150-;;;; General purpose package utilities
   8.151-
   8.152-(eval-when (:load-toplevel :compile-toplevel :execute)
   8.153-  (deftype package-designator () '(and (or package character string symbol) (satisfies find-package)))
   8.154-  (define-condition no-such-package-error (type-error)
   8.155-    ()
   8.156-    (:default-initargs :expected-type 'package-designator)
   8.157-    (:report (lambda (c s)
   8.158-              (format s "No package named ~a" (string (type-error-datum c))))))
   8.159-
   8.160-  (defmethod package-designator ((c no-such-package-error))
   8.161-    (type-error-datum c))
   8.162-
   8.163-  (defun find-package* (package-designator &optional (errorp t))
   8.164-    "Like CL:FIND-PACKAGE, but by default raises a UIOP:NO-SUCH-PACKAGE-ERROR if the
   8.165-  package is not found."
   8.166-    (let ((package (find-package package-designator)))
   8.167-      (cond
   8.168-        (package package)
   8.169-        (errorp (error 'no-such-package-error :datum package-designator))
   8.170-        (t nil))))
   8.171-
   8.172-  (defun find-symbol* (name package-designator &optional (error t))
   8.173-    "Find a symbol in a package of given string'ified NAME;
   8.174-unlike CL:FIND-SYMBOL, work well with 'modern' case sensitive syntax
   8.175-by letting you supply a symbol or keyword for the name;
   8.176-also works well when the package is not present.
   8.177-If optional ERROR argument is NIL, return NIL instead of an error
   8.178-when the symbol is not found."
   8.179-    (block nil
   8.180-      (let ((package (find-package* package-designator error)))
   8.181-        (when package ;; package error handled by find-package* already
   8.182-          (multiple-value-bind (symbol status) (find-symbol (string name) package)
   8.183-            (cond
   8.184-              (status (return (values symbol status)))
   8.185-              (error (error "There is no symbol ~S in package ~S" name (package-name package))))))
   8.186-        (values nil nil))))
   8.187-  (defun symbol-call (package name &rest args)
   8.188-    "Call a function associated with symbol of given name in given package,
   8.189-with given ARGS. Useful when the call is read before the package is loaded,
   8.190-or when loading the package is optional."
   8.191-    (apply (find-symbol* name package) args))
   8.192-  (defun intern* (name package-designator &optional (error t))
   8.193-    (intern (string name) (find-package* package-designator error)))
   8.194-  (defun export* (name package-designator)
   8.195-    (let* ((package (find-package* package-designator))
   8.196-           (symbol (intern* name package)))
   8.197-      (export (or symbol (list symbol)) package)))
   8.198-  (defun import* (symbol package-designator)
   8.199-    (import (or symbol (list symbol)) (find-package* package-designator)))
   8.200-  (defun shadowing-import* (symbol package-designator)
   8.201-    (shadowing-import (or symbol (list symbol)) (find-package* package-designator)))
   8.202-  (defun shadow* (name package-designator)
   8.203-    (shadow (list (string name)) (find-package* package-designator)))
   8.204-  (defun make-symbol* (name)
   8.205-    (etypecase name
   8.206-      (string (make-symbol name))
   8.207-      (symbol (copy-symbol name))))
   8.208-  (defun unintern* (name package-designator &optional (error t))
   8.209-    (block nil
   8.210-      (let ((package (find-package* package-designator error)))
   8.211-        (when package
   8.212-          (multiple-value-bind (symbol status) (find-symbol* name package error)
   8.213-            (cond
   8.214-              (status (unintern symbol package)
   8.215-                      (return (values symbol status)))
   8.216-              (error (error "symbol ~A not present in package ~A"
   8.217-                            (string symbol) (package-name package))))))
   8.218-        (values nil nil))))
   8.219-  (defun symbol-shadowing-p (symbol package)
   8.220-    (and (member symbol (package-shadowing-symbols package)) t))
   8.221-  (defun home-package-p (symbol package)
   8.222-    (and package (let ((sp (symbol-package symbol)))
   8.223-                   (and sp (let ((pp (find-package* package)))
   8.224-                             (and pp (eq sp pp))))))))
   8.225-
   8.226-
   8.227-(eval-when (:load-toplevel :compile-toplevel :execute)
   8.228-  (defun symbol-package-name (symbol)
   8.229-    (let ((package (symbol-package symbol)))
   8.230-      (and package (package-name package))))
   8.231-  (defun standard-common-lisp-symbol-p (symbol)
   8.232-    (multiple-value-bind (sym status) (find-symbol* symbol :common-lisp nil)
   8.233-      (and (eq sym symbol) (eq status :external))))
   8.234-  (defun reify-package (package &optional package-context)
   8.235-    (if (eq package package-context) t
   8.236-        (etypecase package
   8.237-          (null nil)
   8.238-          ((eql (find-package :cl)) :cl)
   8.239-          (package (package-name package)))))
   8.240-  (defun unreify-package (package &optional package-context)
   8.241-    (etypecase package
   8.242-      (null nil)
   8.243-      ((eql t) package-context)
   8.244-      ((or symbol string) (find-package package))))
   8.245-  (defun reify-symbol (symbol &optional package-context)
   8.246-    (etypecase symbol
   8.247-      ((or keyword (satisfies standard-common-lisp-symbol-p)) symbol)
   8.248-      (symbol (vector (symbol-name symbol)
   8.249-                      (reify-package (symbol-package symbol) package-context)))))
   8.250-  (defun unreify-symbol (symbol &optional package-context)
   8.251-    (etypecase symbol
   8.252-      (symbol symbol)
   8.253-      ((simple-vector 2)
   8.254-       (let* ((symbol-name (svref symbol 0))
   8.255-              (package-foo (svref symbol 1))
   8.256-              (package (unreify-package package-foo package-context)))
   8.257-         (if package (intern* symbol-name package)
   8.258-             (make-symbol* symbol-name)))))))
   8.259-
   8.260-(eval-when (:load-toplevel :compile-toplevel :execute)
   8.261-  (defvar *all-package-happiness* '())
   8.262-  (defvar *all-package-fishiness* (list t))
   8.263-  (defun record-fishy (info)
   8.264-    ;;(format t "~&FISHY: ~S~%" info)
   8.265-    (push info *all-package-fishiness*))
   8.266-  (defmacro when-package-fishiness (&body body)
   8.267-    `(when *all-package-fishiness* ,@body))
   8.268-  (defmacro note-package-fishiness (&rest info)
   8.269-    `(when-package-fishiness (record-fishy (list ,@info)))))
   8.270-
   8.271-(eval-when (:load-toplevel :compile-toplevel :execute)
   8.272-  #+(or clisp clozure)
   8.273-  (defun get-setf-function-symbol (symbol)
   8.274-    #+clisp (let ((sym (get symbol 'system::setf-function)))
   8.275-              (if sym (values sym :setf-function)
   8.276-                  (let ((sym (get symbol 'system::setf-expander)))
   8.277-                    (if sym (values sym :setf-expander)
   8.278-                        (values nil nil)))))
   8.279-    #+clozure (gethash symbol ccl::%setf-function-names%))
   8.280-  #+(or clisp clozure)
   8.281-  (defun set-setf-function-symbol (new-setf-symbol symbol &optional kind)
   8.282-    #+clisp (assert (member kind '(:setf-function :setf-expander)))
   8.283-    #+clozure (assert (eq kind t))
   8.284-    #+clisp
   8.285-    (cond
   8.286-      ((null new-setf-symbol)
   8.287-       (remprop symbol 'system::setf-function)
   8.288-       (remprop symbol 'system::setf-expander))
   8.289-      ((eq kind :setf-function)
   8.290-       (setf (get symbol 'system::setf-function) new-setf-symbol))
   8.291-      ((eq kind :setf-expander)
   8.292-       (setf (get symbol 'system::setf-expander) new-setf-symbol))
   8.293-      (t (error "invalid kind of setf-function ~S for ~S to be set to ~S"
   8.294-                kind symbol new-setf-symbol)))
   8.295-    #+clozure
   8.296-    (progn
   8.297-      (gethash symbol ccl::%setf-function-names%) new-setf-symbol
   8.298-      (gethash new-setf-symbol ccl::%setf-function-name-inverses%) symbol))
   8.299-  #+(or clisp clozure)
   8.300-  (defun create-setf-function-symbol (symbol)
   8.301-    #+clisp (system::setf-symbol symbol)
   8.302-    #+clozure (ccl::construct-setf-function-name symbol))
   8.303-  (defun set-dummy-symbol (symbol reason other-symbol)
   8.304-    (setf (get symbol 'dummy-symbol) (cons reason other-symbol)))
   8.305-  (defun make-dummy-symbol (symbol)
   8.306-    (let ((dummy (copy-symbol symbol)))
   8.307-      (set-dummy-symbol dummy 'replacing symbol)
   8.308-      (set-dummy-symbol symbol 'replaced-by dummy)
   8.309-      dummy))
   8.310-  (defun dummy-symbol (symbol)
   8.311-    (get symbol 'dummy-symbol))
   8.312-  (defun get-dummy-symbol (symbol)
   8.313-    (let ((existing (dummy-symbol symbol)))
   8.314-      (if existing (values (cdr existing) (car existing))
   8.315-          (make-dummy-symbol symbol))))
   8.316-  (defun nuke-symbol-in-package (symbol package-designator)
   8.317-    (let ((package (find-package* package-designator))
   8.318-          (name (symbol-name symbol)))
   8.319-      (multiple-value-bind (sym stat) (find-symbol name package)
   8.320-        (when (and (member stat '(:internal :external)) (eq symbol sym))
   8.321-          (if (symbol-shadowing-p symbol package)
   8.322-              (shadowing-import* (get-dummy-symbol symbol) package)
   8.323-              (unintern* symbol package))))))
   8.324-  (defun nuke-symbol (symbol &optional (packages (list-all-packages)))
   8.325-    #+(or clisp clozure)
   8.326-    (multiple-value-bind (setf-symbol kind)
   8.327-        (get-setf-function-symbol symbol)
   8.328-      (when kind (nuke-symbol setf-symbol)))
   8.329-    (loop :for p :in packages :do (nuke-symbol-in-package symbol p)))
   8.330-  (defun rehome-symbol (symbol package-designator)
   8.331-    "Changes the home package of a symbol, also leaving it present in its old home if any"
   8.332-    (let* ((name (symbol-name symbol))
   8.333-           (package (find-package* package-designator))
   8.334-           (old-package (symbol-package symbol))
   8.335-           (old-status (and old-package (nth-value 1 (find-symbol name old-package))))
   8.336-           (shadowing (and old-package (symbol-shadowing-p symbol old-package) (make-symbol name))))
   8.337-      (multiple-value-bind (overwritten-symbol overwritten-symbol-status) (find-symbol name package)
   8.338-        (unless (eq package old-package)
   8.339-          (let ((overwritten-symbol-shadowing-p
   8.340-                  (and overwritten-symbol-status
   8.341-                       (symbol-shadowing-p overwritten-symbol package))))
   8.342-            (note-package-fishiness
   8.343-             :rehome-symbol name
   8.344-             (when old-package (package-name old-package)) old-status (and shadowing t)
   8.345-             (package-name package) overwritten-symbol-status overwritten-symbol-shadowing-p)
   8.346-            (when old-package
   8.347-              (if shadowing
   8.348-                  (shadowing-import* shadowing old-package))
   8.349-              (unintern* symbol old-package))
   8.350-            (cond
   8.351-              (overwritten-symbol-shadowing-p
   8.352-               (shadowing-import* symbol package))
   8.353-              (t
   8.354-               (when overwritten-symbol-status
   8.355-                 (unintern* overwritten-symbol package))
   8.356-               (import* symbol package)))
   8.357-            (if shadowing
   8.358-                (shadowing-import* symbol old-package)
   8.359-                (import* symbol old-package))
   8.360-            #+(or clisp clozure)
   8.361-            (multiple-value-bind (setf-symbol kind)
   8.362-                (get-setf-function-symbol symbol)
   8.363-              (when kind
   8.364-                (let* ((setf-function (fdefinition setf-symbol))
   8.365-                       (new-setf-symbol (create-setf-function-symbol symbol)))
   8.366-                  (note-package-fishiness
   8.367-                   :setf-function
   8.368-                   name (package-name package)
   8.369-                   (symbol-name setf-symbol) (symbol-package-name setf-symbol)
   8.370-                   (symbol-name new-setf-symbol) (symbol-package-name new-setf-symbol))
   8.371-                  (when (symbol-package setf-symbol)
   8.372-                    (unintern* setf-symbol (symbol-package setf-symbol)))
   8.373-                  (setf (fdefinition new-setf-symbol) setf-function)
   8.374-                  (set-setf-function-symbol new-setf-symbol symbol kind))))
   8.375-            #+(or clisp clozure)
   8.376-            (multiple-value-bind (overwritten-setf foundp)
   8.377-                (get-setf-function-symbol overwritten-symbol)
   8.378-              (when foundp
   8.379-                (unintern overwritten-setf)))
   8.380-            (when (eq old-status :external)
   8.381-              (export* symbol old-package))
   8.382-            (when (eq overwritten-symbol-status :external)
   8.383-              (export* symbol package))))
   8.384-        (values overwritten-symbol overwritten-symbol-status))))
   8.385-  (defun ensure-package-unused (package)
   8.386-    (loop :for p :in (package-used-by-list package) :do
   8.387-      (unuse-package package p)))
   8.388-  (defun delete-package* (package &key nuke)
   8.389-    (let ((p (find-package package)))
   8.390-      (when p
   8.391-        (when nuke (do-symbols (s p) (when (home-package-p s p) (nuke-symbol s))))
   8.392-        (ensure-package-unused p)
   8.393-        (delete-package package))))
   8.394-  (defun package-names (package)
   8.395-    (cons (package-name package) (package-nicknames package)))
   8.396-  (defun packages-from-names (names)
   8.397-    (remove-duplicates (remove nil (mapcar #'find-package names)) :from-end t))
   8.398-  (defun fresh-package-name (&key (prefix :%TO-BE-DELETED)
   8.399-                               separator
   8.400-                               (index (random most-positive-fixnum)))
   8.401-    (loop :for i :from index
   8.402-          :for n = (format nil "~A~@[~A~D~]" prefix (and (plusp i) (or separator "")) i)
   8.403-          :thereis (and (not (find-package n)) n)))
   8.404-  (defun rename-package-away (p &rest keys &key prefix &allow-other-keys)
   8.405-    (let ((new-name
   8.406-            (apply 'fresh-package-name
   8.407-                   :prefix (or prefix (format nil "__~A__" (package-name p))) keys)))
   8.408-      (record-fishy (list :rename-away (package-names p) new-name))
   8.409-      (rename-package p new-name))))
   8.410-
   8.411-
   8.412-;;; Communicable representation of symbol and package information
   8.413-
   8.414-(eval-when (:load-toplevel :compile-toplevel :execute)
   8.415-  (defun package-definition-form (package-designator
   8.416-                                  &key (nicknamesp t) (usep t)
   8.417-                                    (shadowp t) (shadowing-import-p t)
   8.418-                                    (exportp t) (importp t) internp (error t))
   8.419-    (let* ((package (or (find-package* package-designator error)
   8.420-                        (return-from package-definition-form nil)))
   8.421-           (name (package-name package))
   8.422-           (nicknames (package-nicknames package))
   8.423-           (use (mapcar #'package-name (package-use-list package)))
   8.424-           (shadow ())
   8.425-           (shadowing-import (make-hash-table :test 'equal))
   8.426-           (import (make-hash-table :test 'equal))
   8.427-           (export ())
   8.428-           (intern ()))
   8.429-      (when package
   8.430-        (loop :for sym :being :the :symbols :in package
   8.431-              :for status = (nth-value 1 (find-symbol* sym package)) :do
   8.432-                (ecase status
   8.433-                  ((nil :inherited))
   8.434-                  ((:internal :external)
   8.435-                   (let* ((name (symbol-name sym))
   8.436-                          (external (eq status :external))
   8.437-                          (home (symbol-package sym))
   8.438-                          (home-name (package-name home))
   8.439-                          (imported (not (eq home package)))
   8.440-                          (shadowing (symbol-shadowing-p sym package)))
   8.441-                     (cond
   8.442-                       ((and shadowing imported)
   8.443-                        (push name (gethash home-name shadowing-import)))
   8.444-                       (shadowing
   8.445-                        (push name shadow))
   8.446-                       (imported
   8.447-                        (push name (gethash home-name import))))
   8.448-                     (cond
   8.449-                       (external
   8.450-                        (push name export))
   8.451-                       (imported)
   8.452-                       (t (push name intern)))))))
   8.453-        (labels ((sort-names (names)
   8.454-                   (sort (copy-list names) #'string<))
   8.455-                 (table-keys (table)
   8.456-                   (loop :for k :being :the :hash-keys :of table :collect k))
   8.457-                 (when-relevant (key value)
   8.458-                   (when value (list (cons key value))))
   8.459-                 (import-options (key table)
   8.460-                   (loop :for i :in (sort-names (table-keys table))
   8.461-                         :collect `(,key ,i ,@(sort-names (gethash i table))))))
   8.462-          `(defpackage ,name
   8.463-             ,@(when-relevant :nicknames (and nicknamesp (sort-names nicknames)))
   8.464-             (:use ,@(and usep (sort-names use)))
   8.465-             ,@(when-relevant :shadow (and shadowp (sort-names shadow)))
   8.466-             ,@(import-options :shadowing-import-from (and shadowing-import-p shadowing-import))
   8.467-             ,@(import-options :import-from (and importp import))
   8.468-             ,@(when-relevant :export (and exportp (sort-names export)))
   8.469-             ,@(when-relevant :intern (and internp (sort-names intern)))))))))
   8.470-
   8.471-
   8.472-;;; ensure-package, define-package
   8.473-(eval-when (:load-toplevel :compile-toplevel :execute)
   8.474-  ;; We already have UIOP:SIMPLE-STYLE-WARNING, but it comes from a later
   8.475-  ;; package.
   8.476-  (define-condition define-package-style-warning
   8.477-      #+sbcl (sb-int:simple-style-warning) #-sbcl (simple-condition style-warning)
   8.478-      ())
   8.479-  (defun ensure-shadowing-import (name to-package from-package shadowed imported)
   8.480-    (check-type name string)
   8.481-    (check-type to-package package)
   8.482-    (check-type from-package package)
   8.483-    (check-type shadowed hash-table)
   8.484-    (check-type imported hash-table)
   8.485-    (let ((import-me (find-symbol* name from-package)))
   8.486-      (multiple-value-bind (existing status) (find-symbol name to-package)
   8.487-        (cond
   8.488-          ((gethash name shadowed)
   8.489-           (unless (eq import-me existing)
   8.490-             (error "Conflicting shadowings for ~A" name)))
   8.491-          (t
   8.492-           (setf (gethash name shadowed) t)
   8.493-           (setf (gethash name imported) t)
   8.494-           (unless (or (null status)
   8.495-                       (and (member status '(:internal :external))
   8.496-                            (eq existing import-me)
   8.497-                            (symbol-shadowing-p existing to-package)))
   8.498-             (note-package-fishiness
   8.499-              :shadowing-import name
   8.500-              (package-name from-package)
   8.501-              (or (home-package-p import-me from-package) (symbol-package-name import-me))
   8.502-              (package-name to-package) status
   8.503-              (and status (or (home-package-p existing to-package) (symbol-package-name existing)))))
   8.504-           (shadowing-import* import-me to-package))))))
   8.505-  (defun ensure-imported (import-me into-package &optional from-package)
   8.506-    (check-type import-me symbol)
   8.507-    (check-type into-package package)
   8.508-    (check-type from-package (or null package))
   8.509-    (let ((name (symbol-name import-me)))
   8.510-      (multiple-value-bind (existing status) (find-symbol name into-package)
   8.511-        (cond
   8.512-          ((not status)
   8.513-           (import* import-me into-package))
   8.514-          ((eq import-me existing))
   8.515-          (t
   8.516-           (let ((shadowing-p (symbol-shadowing-p existing into-package)))
   8.517-             (note-package-fishiness
   8.518-              :ensure-imported name
   8.519-              (and from-package (package-name from-package))
   8.520-              (or (home-package-p import-me from-package) (symbol-package-name import-me))
   8.521-              (package-name into-package)
   8.522-              status
   8.523-              (and status (or (home-package-p existing into-package) (symbol-package-name existing)))
   8.524-              shadowing-p)
   8.525-             (cond
   8.526-               ((or shadowing-p (eq status :inherited))
   8.527-                (shadowing-import* import-me into-package))
   8.528-               (t
   8.529-                (unintern* existing into-package)
   8.530-                (import* import-me into-package))))))))
   8.531-    (values))
   8.532-  (defun ensure-import (name to-package from-package shadowed imported)
   8.533-    (check-type name string)
   8.534-    (check-type to-package package)
   8.535-    (check-type from-package package)
   8.536-    (check-type shadowed hash-table)
   8.537-    (check-type imported hash-table)
   8.538-    (multiple-value-bind (import-me import-status) (find-symbol name from-package)
   8.539-      (when (null import-status)
   8.540-        (note-package-fishiness
   8.541-         :import-uninterned name (package-name from-package) (package-name to-package))
   8.542-        (setf import-me (intern* name from-package)))
   8.543-      (multiple-value-bind (existing status) (find-symbol name to-package)
   8.544-        (cond
   8.545-          ((and imported (gethash name imported))
   8.546-           (unless (and status (eq import-me existing))
   8.547-             (error "Can't import ~S from both ~S and ~S"
   8.548-                    name (package-name (symbol-package existing)) (package-name from-package))))
   8.549-          ((gethash name shadowed)
   8.550-           (error "Can't both shadow ~S and import it from ~S" name (package-name from-package)))
   8.551-          (t
   8.552-           (setf (gethash name imported) t))))
   8.553-      (ensure-imported import-me to-package from-package)))
   8.554-  (defun ensure-inherited (name symbol to-package from-package mixp shadowed imported inherited)
   8.555-    (check-type name string)
   8.556-    (check-type symbol symbol)
   8.557-    (check-type to-package package)
   8.558-    (check-type from-package package)
   8.559-    (check-type mixp (member nil t)) ; no cl:boolean on Genera
   8.560-    (check-type shadowed hash-table)
   8.561-    (check-type imported hash-table)
   8.562-    (check-type inherited hash-table)
   8.563-    (multiple-value-bind (existing status) (find-symbol name to-package)
   8.564-      (let* ((sp (symbol-package symbol))
   8.565-             (in (gethash name inherited))
   8.566-             (xp (and status (symbol-package existing))))
   8.567-        (when (null sp)
   8.568-          (note-package-fishiness
   8.569-           :import-uninterned name
   8.570-           (package-name from-package) (package-name to-package) mixp)
   8.571-          (import* symbol from-package)
   8.572-          (setf sp (package-name from-package)))
   8.573-        (cond
   8.574-          ((gethash name shadowed))
   8.575-          (in
   8.576-           (unless (equal sp (first in))
   8.577-             (if mixp
   8.578-                 (ensure-shadowing-import name to-package (second in) shadowed imported)
   8.579-                 (error "Can't inherit ~S from ~S, it is inherited from ~S"
   8.580-                        name (package-name sp) (package-name (first in))))))
   8.581-          ((gethash name imported)
   8.582-           (unless (eq symbol existing)
   8.583-             (error "Can't inherit ~S from ~S, it is imported from ~S"
   8.584-                    name (package-name sp) (package-name xp))))
   8.585-          (t
   8.586-           (setf (gethash name inherited) (list sp from-package))
   8.587-           (when (and status (not (eq sp xp)))
   8.588-             (let ((shadowing (symbol-shadowing-p existing to-package)))
   8.589-               (note-package-fishiness
   8.590-                :inherited name
   8.591-                (package-name from-package)
   8.592-                (or (home-package-p symbol from-package) (symbol-package-name symbol))
   8.593-                (package-name to-package)
   8.594-                (or (home-package-p existing to-package) (symbol-package-name existing)))
   8.595-               (if shadowing (ensure-shadowing-import name to-package from-package shadowed imported)
   8.596-                   (unintern* existing to-package)))))))))
   8.597-  (defun ensure-mix (name symbol to-package from-package shadowed imported inherited)
   8.598-    (check-type name string)
   8.599-    (check-type symbol symbol)
   8.600-    (check-type to-package package)
   8.601-    (check-type from-package package)
   8.602-    (check-type shadowed hash-table)
   8.603-    (check-type imported hash-table)
   8.604-    (check-type inherited hash-table)
   8.605-    (unless (gethash name shadowed)
   8.606-      (multiple-value-bind (existing status) (find-symbol name to-package)
   8.607-        (let* ((sp (symbol-package symbol))
   8.608-               (im (gethash name imported))
   8.609-               (in (gethash name inherited)))
   8.610-          (cond
   8.611-            ((or (null status)
   8.612-                 (and status (eq symbol existing))
   8.613-                 (and in (eq sp (first in))))
   8.614-             (ensure-inherited name symbol to-package from-package t shadowed imported inherited))
   8.615-            (in
   8.616-             (remhash name inherited)
   8.617-             (ensure-shadowing-import name to-package (second in) shadowed imported))
   8.618-            (im
   8.619-             (error "Symbol ~S import from ~S~:[~; actually ~:[uninterned~;~:*from ~S~]~] conflicts with existing symbol in ~S~:[~; actually ~:[uninterned~;from ~:*~S~]~]"
   8.620-                    name (package-name from-package)
   8.621-                    (home-package-p symbol from-package) (symbol-package-name symbol)
   8.622-                    (package-name to-package)
   8.623-                    (home-package-p existing to-package) (symbol-package-name existing)))
   8.624-            (t
   8.625-             (ensure-inherited name symbol to-package from-package t shadowed imported inherited)))))))
   8.626-
   8.627-  (defun recycle-symbol (name recycle exported)
   8.628-    ;; Takes a symbol NAME (a string), a list of package designators for RECYCLE
   8.629-    ;; packages, and a hash-table of names (strings) of symbols scheduled to be
   8.630-    ;; EXPORTED from the package being defined. It returns two values, the
   8.631-    ;; symbol found (if any, or else NIL), and a boolean flag indicating whether
   8.632-    ;; a symbol was found. The caller (DEFINE-PACKAGE) will then do the
   8.633-    ;; re-homing of the symbol, etc.
   8.634-    (check-type name string)
   8.635-    (check-type recycle list)
   8.636-    (check-type exported hash-table)
   8.637-    (when (gethash name exported) ;; don't bother recycling private symbols
   8.638-      (let (recycled foundp)
   8.639-        (dolist (r recycle (values recycled foundp))
   8.640-          (multiple-value-bind (symbol status) (find-symbol name r)
   8.641-            (when (and status (home-package-p symbol r))
   8.642-              (cond
   8.643-                (foundp
   8.644-                 ;; (nuke-symbol symbol)) -- even simple variable names like O or C will do that.
   8.645-                 (note-package-fishiness :recycled-duplicate name (package-name foundp) (package-name r)))
   8.646-                (t
   8.647-                 (setf recycled symbol foundp r)))))))))
   8.648-  (defun symbol-recycled-p (sym recycle)
   8.649-    (check-type sym symbol)
   8.650-    (check-type recycle list)
   8.651-    (and (member (symbol-package sym) recycle) t))
   8.652-  (defun ensure-symbol (name package intern recycle shadowed imported inherited exported)
   8.653-    (check-type name string)
   8.654-    (check-type package package)
   8.655-    (check-type intern (member nil t)) ; no cl:boolean on Genera
   8.656-    (check-type shadowed hash-table)
   8.657-    (check-type imported hash-table)
   8.658-    (check-type inherited hash-table)
   8.659-    (unless (or (gethash name shadowed)
   8.660-                (gethash name imported)
   8.661-                (gethash name inherited))
   8.662-      (multiple-value-bind (existing status)
   8.663-          (find-symbol name package)
   8.664-        (multiple-value-bind (recycled previous) (recycle-symbol name recycle exported)
   8.665-          (cond
   8.666-            ((and status (eq existing recycled) (eq previous package)))
   8.667-            (previous
   8.668-             (rehome-symbol recycled package))
   8.669-            ((and status (eq package (symbol-package existing))))
   8.670-            (t
   8.671-             (when status
   8.672-               (note-package-fishiness
   8.673-                :ensure-symbol name
   8.674-                (reify-package (symbol-package existing) package)
   8.675-                status intern)
   8.676-               (unintern existing))
   8.677-             (when intern
   8.678-               (intern* name package))))))))
   8.679-  (declaim (ftype (function (t t t &optional t) t) ensure-exported))
   8.680-  (defun ensure-exported-to-user (name symbol to-package &optional recycle)
   8.681-    (check-type name string)
   8.682-    (check-type symbol symbol)
   8.683-    (check-type to-package package)
   8.684-    (check-type recycle list)
   8.685-    (assert (equal name (symbol-name symbol)))
   8.686-    (multiple-value-bind (existing status) (find-symbol name to-package)
   8.687-      (unless (and status (eq symbol existing))
   8.688-        (let ((accessible
   8.689-                (or (null status)
   8.690-                    (let ((shadowing (symbol-shadowing-p existing to-package))
   8.691-                          (recycled (symbol-recycled-p existing recycle)))
   8.692-                      (unless (and shadowing (not recycled))
   8.693-                        (note-package-fishiness
   8.694-                         :ensure-export name (symbol-package-name symbol)
   8.695-                         (package-name to-package)
   8.696-                         (or (home-package-p existing to-package) (symbol-package-name existing))
   8.697-                         status shadowing)
   8.698-                        (if (or (eq status :inherited) shadowing)
   8.699-                            (shadowing-import* symbol to-package)
   8.700-                            (unintern existing to-package))
   8.701-                        t)))))
   8.702-          (when (and accessible (eq status :external))
   8.703-            (ensure-exported name symbol to-package recycle))))))
   8.704-  (defun ensure-exported (name symbol from-package &optional recycle)
   8.705-    (dolist (to-package (package-used-by-list from-package))
   8.706-      (ensure-exported-to-user name symbol to-package recycle))
   8.707-    (unless (eq from-package (symbol-package symbol))
   8.708-      (ensure-imported symbol from-package))
   8.709-    (export* name from-package))
   8.710-  (defun ensure-export (name from-package &optional recycle)
   8.711-    (multiple-value-bind (symbol status) (find-symbol* name from-package)
   8.712-      (unless (eq status :external)
   8.713-        (ensure-exported name symbol from-package recycle))))
   8.714-
   8.715-  #+package-local-nicknames
   8.716-  (defun install-package-local-nicknames (destination-package new-nicknames)
   8.717-    ;; First, remove all package-local nicknames. (We'll reinstall any desired ones later.)
   8.718-    (dolist (pair-to-remove (uiop/package-local-nicknames:package-local-nicknames destination-package))
   8.719-      (uiop/package-local-nicknames:remove-package-local-nickname
   8.720-       (string (car pair-to-remove)) destination-package))
   8.721-    ;; Then, install all desired nicknames.
   8.722-    (loop :for (nickname package) :in new-nicknames
   8.723-          :do (uiop/package-local-nicknames:add-package-local-nickname
   8.724-               (string nickname)
   8.725-               (find-package package)
   8.726-               destination-package)))
   8.727-
   8.728-  (defun ensure-package (name &key
   8.729-                                nicknames documentation use
   8.730-                                shadow shadowing-import-from
   8.731-                                import-from export intern
   8.732-                                recycle mix reexport
   8.733-                                unintern local-nicknames)
   8.734-    #+genera (declare (ignore documentation))
   8.735-    (let* ((package-name (string name))
   8.736-           (nicknames (mapcar #'string nicknames))
   8.737-           (names (cons package-name nicknames))
   8.738-           (previous (packages-from-names names))
   8.739-           (discarded (cdr previous))
   8.740-           (to-delete ())
   8.741-           (package (or (first previous) (make-package package-name :nicknames nicknames)))
   8.742-           (recycle (packages-from-names recycle))
   8.743-           (use (mapcar 'find-package* use))
   8.744-           (mix (mapcar 'find-package* mix))
   8.745-           (reexport (mapcar 'find-package* reexport))
   8.746-           (shadow (mapcar 'string shadow))
   8.747-           (export (mapcar 'string export))
   8.748-           (intern (mapcar 'string intern))
   8.749-           (unintern (mapcar 'string unintern))
   8.750-           (local-nicknames (mapcar #'(lambda (pair) (mapcar 'string pair)) local-nicknames))
   8.751-           (shadowed (make-hash-table :test 'equal)) ; string to bool
   8.752-           (imported (make-hash-table :test 'equal)) ; string to bool
   8.753-           (exported (make-hash-table :test 'equal)) ; string to bool
   8.754-           ;; string to list home package and use package:
   8.755-           (inherited (make-hash-table :test 'equal)))
   8.756-      #-package-local-nicknames
   8.757-      (declare (ignore local-nicknames)) ; if not supported
   8.758-      (when-package-fishiness (record-fishy package-name))
   8.759-      ;; if supported, put package documentation
   8.760-      #-genera
   8.761-      (when documentation (setf (documentation package t) documentation))
   8.762-      ;; remove unwanted packages from use list
   8.763-      (loop :for p :in (set-difference (package-use-list package) (append mix use))
   8.764-            :do (note-package-fishiness :over-use name (package-names p))
   8.765-                (unuse-package p package))
   8.766-      ;; mark unwanted packages for deletion
   8.767-      (loop :for p :in discarded
   8.768-            :for n = (remove-if #'(lambda (x) (member x names :test 'equal))
   8.769-                                (package-names p))
   8.770-            :do (note-package-fishiness :nickname name (package-names p))
   8.771-                (cond (n (rename-package p (first n) (rest n)))
   8.772-                      (t (rename-package-away p)
   8.773-                         (push p to-delete))))
   8.774-      ;; give package its desired name
   8.775-      (rename-package package package-name nicknames)
   8.776-      ;; Handle local nicknames
   8.777-      #+package-local-nicknames
   8.778-      (install-package-local-nicknames package local-nicknames)
   8.779-      (dolist (name unintern)
   8.780-        (multiple-value-bind (existing status) (find-symbol name package)
   8.781-          (when status
   8.782-            (unless (eq status :inherited)
   8.783-              (note-package-fishiness
   8.784-               :unintern (package-name package) name (symbol-package-name existing) status)
   8.785-              (unintern* name package nil)))))
   8.786-      ;; handle exports
   8.787-      (dolist (name export)
   8.788-        (setf (gethash name exported) t))
   8.789-      ;; handle reexportss
   8.790-      (dolist (p reexport)
   8.791-        (do-external-symbols (sym p)
   8.792-          (setf (gethash (string sym) exported) t)))
   8.793-      ;; unexport symbols not listed in (re)export
   8.794-      (do-external-symbols (sym package)
   8.795-        (let ((name (symbol-name sym)))
   8.796-          (unless (gethash name exported)
   8.797-            (note-package-fishiness
   8.798-             :over-export (package-name package) name
   8.799-             (or (home-package-p sym package) (symbol-package-name sym)))
   8.800-            (unexport sym package))))
   8.801-      ;; handle explicitly listed shadowed ssymbols
   8.802-      (dolist (name shadow)
   8.803-        (setf (gethash name shadowed) t)
   8.804-        (multiple-value-bind (existing status) (find-symbol name package)
   8.805-          (multiple-value-bind (recycled previous) (recycle-symbol name recycle exported)
   8.806-            (let ((shadowing (and status (symbol-shadowing-p existing package))))
   8.807-              (cond
   8.808-                ((eq previous package))
   8.809-                (previous
   8.810-                 (rehome-symbol recycled package))
   8.811-                ((or (member status '(nil :inherited))
   8.812-                     (home-package-p existing package)))
   8.813-                (t
   8.814-                 (let ((dummy (make-symbol name)))
   8.815-                   (note-package-fishiness
   8.816-                    :shadow-imported (package-name package) name
   8.817-                    (symbol-package-name existing) status shadowing)
   8.818-                   (shadowing-import* dummy package)
   8.819-                   (import* dummy package)))))))
   8.820-        (shadow* name package))
   8.821-      ;; handle shadowing imports
   8.822-      (loop :for (p . syms) :in shadowing-import-from
   8.823-            :for pp = (find-package* p) :do
   8.824-              (dolist (sym syms) (ensure-shadowing-import (string sym) package pp shadowed imported)))
   8.825-      ;; handle mixed packages
   8.826-      (loop :for p :in mix
   8.827-            :for pp = (find-package* p) :do
   8.828-              (do-external-symbols (sym pp) (ensure-mix (symbol-name sym) sym package pp shadowed imported inherited)))
   8.829-      ;; handle import-from packages
   8.830-      (loop :for (p . syms) :in import-from
   8.831-            ;; FOR NOW suppress errors in the case where the :import-from
   8.832-            ;; symbol list is empty (used only to establish a dependency by
   8.833-            ;; package-inferred-system users).
   8.834-            :for pp = (find-package* p syms) :do
   8.835-              (when (null pp)
   8.836-                ;; TODO: ASDF 3.4 Change to a full warning.
   8.837-                (warn 'define-package-style-warning
   8.838-                      :format-control "When defining package ~a, attempting to import-from non-existent package ~a. This is deprecated behavior and will be removed from UIOP in the future."
   8.839-                      :format-arguments (list name p)))
   8.840-              (dolist (sym syms) (ensure-import (symbol-name sym) package pp shadowed imported)))
   8.841-      ;; handle use-list and mix
   8.842-      (dolist (p (append use mix))
   8.843-        (do-external-symbols (sym p) (ensure-inherited (string sym) sym package p nil shadowed imported inherited))
   8.844-        (use-package p package))
   8.845-      (loop :for name :being :the :hash-keys :of exported :do
   8.846-        (ensure-symbol name package t recycle shadowed imported inherited exported)
   8.847-        (ensure-export name package recycle))
   8.848-      ;; intern dessired symbols
   8.849-      (dolist (name intern)
   8.850-        (ensure-symbol name package t recycle shadowed imported inherited exported))
   8.851-      (do-symbols (sym package)
   8.852-        (ensure-symbol (symbol-name sym) package nil recycle shadowed imported inherited exported))
   8.853-      ;; delete now-deceased packages
   8.854-      (map () 'delete-package* to-delete)
   8.855-      package)))
   8.856-
   8.857-
   8.858-(eval-when (:load-toplevel :compile-toplevel :execute)
   8.859-  (defun parse-define-package-form (package clauses)
   8.860-    (loop
   8.861-      :with use-p = nil :with recycle-p = nil
   8.862-      :with documentation = nil
   8.863-      :for (kw . args) :in clauses
   8.864-      :when (eq kw :nicknames) :append args :into nicknames :else
   8.865-      :when (eq kw :documentation)
   8.866-        :do (cond
   8.867-              (documentation (error "define-package: can't define documentation twice"))
   8.868-              ((or (atom args) (cdr args)) (error "define-package: bad documentation"))
   8.869-              (t (setf documentation (car args)))) :else
   8.870-      :when (eq kw :use) :append args :into use :and :do (setf use-p t) :else
   8.871-      :when (eq kw :shadow) :append args :into shadow :else
   8.872-      :when (eq kw :shadowing-import-from) :collect args :into shadowing-import-from :else
   8.873-      :when (eq kw :import-from) :collect args :into import-from :else
   8.874-      :when (eq kw :export) :append args :into export :else
   8.875-      :when (eq kw :intern) :append args :into intern :else
   8.876-      :when (eq kw :recycle) :append args :into recycle :and :do (setf recycle-p t) :else
   8.877-      :when (eq kw :mix) :append args :into mix :else
   8.878-      :when (eq kw :reexport) :append args :into reexport :else
   8.879-      :when (eq kw :use-reexport) :append args :into use :and :append args :into reexport
   8.880-        :and :do (setf use-p t) :else
   8.881-      :when (eq kw :mix-reexport) :append args :into mix :and :append args :into reexport
   8.882-        :and :do (setf use-p t) :else
   8.883-      :when (eq kw :unintern) :append args :into unintern :else
   8.884-      :when (eq kw :local-nicknames)
   8.885-        :if (symbol-call '#:uiop '#:featurep :package-local-nicknames)
   8.886-          :append args :into local-nicknames
   8.887-        :else
   8.888-          :do (error ":LOCAL-NICKAMES option is not supported on this lisp implementation.")
   8.889-        :end
   8.890-      :else
   8.891-        :do (error "unrecognized define-package keyword ~S" kw)
   8.892-      :finally (return `(',package
   8.893-                         :nicknames ',nicknames :documentation ',documentation
   8.894-                         :use ',(if use-p use '(:common-lisp))
   8.895-                         :shadow ',shadow :shadowing-import-from ',shadowing-import-from
   8.896-                         :import-from ',import-from :export ',export :intern ',intern
   8.897-                         :recycle ',(if recycle-p recycle (cons package nicknames))
   8.898-                         :mix ',mix :reexport ',reexport :unintern ',unintern
   8.899-                         ,@(when local-nicknames
   8.900-                             `(:local-nicknames ',local-nicknames)))))))
   8.901-
   8.902-(defmacro define-package (package &rest clauses)
   8.903-  "DEFINE-PACKAGE takes a PACKAGE and a number of CLAUSES, of the form
   8.904-\(KEYWORD . ARGS\).
   8.905-DEFINE-PACKAGE supports the following keywords:
   8.906-SHADOW, SHADOWING-IMPORT-FROM, IMPORT-FROM, EXPORT, INTERN, NICKNAMES,
   8.907-DOCUMENTATION -- as per CL:DEFPACKAGE.
   8.908-USE -- as per CL:DEFPACKAGE, but if neither USE, USE-REEXPORT, MIX,
   8.909-nor MIX-REEXPORT is supplied, then it is equivalent to specifying
   8.910-(:USE :COMMON-LISP). This is unlike CL:DEFPACKAGE for which the
   8.911-behavior of a form without USE is implementation-dependent.
   8.912-RECYCLE -- Recycle the package's exported symbols from the specified packages,
   8.913-in order.  For every symbol scheduled to be exported by the DEFINE-PACKAGE,
   8.914-either through an :EXPORT option or a :REEXPORT option, if the symbol exists in
   8.915-one of the :RECYCLE packages, the first such symbol is re-homed to the package
   8.916-being defined.
   8.917-For the sake of idempotence, it is important that the package being defined
   8.918-should appear in first position if it already exists, and even if it doesn't,
   8.919-ahead of any package that is not going to be deleted afterwards and never
   8.920-created again. In short, except for special cases, always make it the first
   8.921-package on the list if the list is not empty.
   8.922-MIX -- Takes a list of package designators.  MIX behaves like
   8.923-\(:USE PKG1 PKG2 ... PKGn\) but additionally uses :SHADOWING-IMPORT-FROM to
   8.924-resolve conflicts in favor of the first found symbol.  It may still yield
   8.925-an error if there is a conflict with an explicitly :IMPORT-FROM symbol.
   8.926-REEXPORT -- Takes a list of package designators.  For each package, p, in the list,
   8.927-export symbols with the same name as those exported from p.  Note that in the case
   8.928-of shadowing, etc. the symbols with the same name may not be the same symbols.
   8.929-UNINTERN -- Remove symbols here from PACKAGE. Note that this is primarily useful
   8.930-when *redefining* a previously-existing package in the current image (e.g., when
   8.931-upgrading ASDF).  Most programmers will have no use for this option.
   8.932-LOCAL-NICKNAMES -- If the host implementation supports package local nicknames
   8.933-\(check for the :PACKAGE-LOCAL-NICKNAMES feature\), then this should be a list of
   8.934-nickname and package name pairs.  Using this option will cause an error if the
   8.935-host CL implementation does not support it.
   8.936-USE-REEXPORT, MIX-REEXPORT -- Use or mix the specified packages as per the USE or
   8.937-MIX directives, and reexport their contents as per the REEXPORT directive."
   8.938-  (let ((ensure-form
   8.939-         `(prog1
   8.940-              (funcall 'ensure-package ,@(parse-define-package-form package clauses))
   8.941-            #+sbcl (setf (sb-impl::package-source-location (find-package ',package))
   8.942-                         (sb-c:source-location)))))
   8.943-    `(progn
   8.944-       #+(or clasp ecl gcl mkcl) (defpackage ,package (:use))
   8.945-       (eval-when (:compile-toplevel :load-toplevel :execute)
   8.946-         ,ensure-form))))
   8.947-
   8.948-;; This package, unlike UIOP/PACKAGE, is allowed to evolve and acquire new symbols or drop old ones.
   8.949-(define-package :uiop/package*
   8.950-  (:use-reexport :uiop/package
   8.951-                 #+package-local-nicknames :uiop/package-local-nicknames)
   8.952-  (:import-from :uiop/package
   8.953-                #:define-package-style-warning
   8.954-                #:no-such-package-error
   8.955-                #:package-designator)
   8.956-  (:export #:define-package-style-warning
   8.957-           #:no-such-package-error
   8.958-           #:package-designator))
   8.959-;;;; -------------------------------------------------------------------------
   8.960-;;;; Handle compatibility with multiple implementations.
   8.961-;;; This file is for papering over the deficiencies and peculiarities
   8.962-;;; of various Common Lisp implementations.
   8.963-;;; For implementation-specific access to the system, see os.lisp instead.
   8.964-;;; A few functions are defined here, but actually exported from utility;
   8.965-;;; from this package only common-lisp symbols are exported.
   8.966-
   8.967-(uiop/package:define-package :uiop/common-lisp
   8.968-  (:nicknames :uiop/cl)
   8.969-  (:use :uiop/package)
   8.970-  (:use-reexport #-genera :common-lisp #+genera :future-common-lisp)
   8.971-  #+allegro (:intern #:*acl-warn-save*)
   8.972-  #+cormanlisp (:shadow #:user-homedir-pathname)
   8.973-  #+cormanlisp
   8.974-  (:export
   8.975-   #:logical-pathname #:translate-logical-pathname
   8.976-   #:make-broadcast-stream #:file-namestring)
   8.977-  #+genera (:shadowing-import-from :scl #:boolean)
   8.978-  #+genera (:export #:boolean #:ensure-directories-exist #:read-sequence #:write-sequence)
   8.979-  #+(or mcl cmucl) (:shadow #:user-homedir-pathname))
   8.980-(in-package :uiop/common-lisp)
   8.981-
   8.982-#-(or abcl allegro clasp clisp clozure cmucl cormanlisp ecl gcl genera lispworks mcl mezzano mkcl sbcl scl xcl)
   8.983-(error "ASDF is not supported on your implementation. Please help us port it.")
   8.984-
   8.985-;; (declaim (optimize (speed 1) (debug 3) (safety 3))) ; DON'T: trust implementation defaults.
   8.986-
   8.987-
   8.988-;;;; Early meta-level tweaks
   8.989-
   8.990-#+(or allegro clasp clisp clozure cmucl ecl lispworks mezzano mkcl sbcl abcl)
   8.991-(eval-when (:load-toplevel :compile-toplevel :execute)
   8.992-  (when (and #+allegro (member :ics *features*)
   8.993-             #+(or clasp clisp cmucl ecl lispworks mkcl) (member :unicode *features*)
   8.994-             #+clozure (member :openmcl-unicode-strings *features*)
   8.995-             #+sbcl (member :sb-unicode *features*)
   8.996-             #+abcl t)
   8.997-    ;; Check for unicode at runtime, so that a hypothetical FASL compiled with unicode
   8.998-    ;; but loaded in a non-unicode setting (e.g. on Allegro) won't tell a lie.
   8.999-    (pushnew :asdf-unicode *features*)))
  8.1000-
  8.1001-#+allegro
  8.1002-(eval-when (:load-toplevel :compile-toplevel :execute)
  8.1003-  ;; We need to disable autoloading BEFORE any mention of package ASDF.
  8.1004-  ;; In particular, there must NOT be a mention of package ASDF in the defpackage of this file
  8.1005-  ;; or any previous file.
  8.1006-  (setf excl::*autoload-package-name-alist*
  8.1007-        (remove "asdf" excl::*autoload-package-name-alist*
  8.1008-                :test 'equalp :key 'car))
  8.1009-  (defparameter *acl-warn-save*
  8.1010-    (when (boundp 'excl:*warn-on-nested-reader-conditionals*)
  8.1011-      excl:*warn-on-nested-reader-conditionals*))
  8.1012-  (when (boundp 'excl:*warn-on-nested-reader-conditionals*)
  8.1013-    (setf excl:*warn-on-nested-reader-conditionals* nil))
  8.1014-  (setf *print-readably* nil))
  8.1015-
  8.1016-#+clasp
  8.1017-(eval-when (:load-toplevel :compile-toplevel :execute)
  8.1018-  (setf *load-verbose* nil)
  8.1019-  (defun use-ecl-byte-compiler-p () nil))
  8.1020-
  8.1021-#+clozure (in-package :ccl)
  8.1022-#+(and clozure windows-target) ;; See http://trac.clozure.com/ccl/ticket/1117
  8.1023-(eval-when (:load-toplevel :compile-toplevel :execute)
  8.1024-  (unless (fboundp 'external-process-wait)
  8.1025-    (in-development-mode
  8.1026-     (defun external-process-wait (proc)
  8.1027-       (when (and (external-process-pid proc) (eq (external-process-%status proc) :running))
  8.1028-         (with-interrupts-enabled
  8.1029-             (wait-on-semaphore (external-process-completed proc))))
  8.1030-       (values (external-process-%exit-code proc)
  8.1031-               (external-process-%status proc))))))
  8.1032-#+clozure (in-package :uiop/common-lisp) ;; back in this package.
  8.1033-
  8.1034-#+cmucl
  8.1035-(eval-when (:load-toplevel :compile-toplevel :execute)
  8.1036-  (setf ext:*gc-verbose* nil)
  8.1037-  (defun user-homedir-pathname ()
  8.1038-    (first (ext:search-list (cl:user-homedir-pathname)))))
  8.1039-
  8.1040-#+cormanlisp
  8.1041-(eval-when (:load-toplevel :compile-toplevel :execute)
  8.1042-  (deftype logical-pathname () nil)
  8.1043-  (defun make-broadcast-stream () *error-output*)
  8.1044-  (defun translate-logical-pathname (x) x)
  8.1045-  (defun user-homedir-pathname (&optional host)
  8.1046-    (declare (ignore host))
  8.1047-    (parse-namestring (format nil "~A\\" (cl:user-homedir-pathname))))
  8.1048-  (defun file-namestring (p)
  8.1049-    (setf p (pathname p))
  8.1050-    (format nil "~@[~A~]~@[.~A~]" (pathname-name p) (pathname-type p))))
  8.1051-
  8.1052-#+ecl
  8.1053-(eval-when (:load-toplevel :compile-toplevel :execute)
  8.1054-  (setf *load-verbose* nil)
  8.1055-  (defun use-ecl-byte-compiler-p () (and (member :ecl-bytecmp *features*) t))
  8.1056-  (unless (use-ecl-byte-compiler-p) (require :cmp)))
  8.1057-
  8.1058-#+gcl
  8.1059-(eval-when (:load-toplevel :compile-toplevel :execute)
  8.1060-  (unless (member :ansi-cl *features*)
  8.1061-    (error "ASDF only supports GCL in ANSI mode. Aborting.~%"))
  8.1062-  (setf compiler::*compiler-default-type* (pathname "")
  8.1063-        compiler::*lsp-ext* "")
  8.1064-  #.(let ((code ;; Only support very recent GCL 2.7.0 from November 2013 or later.
  8.1065-            (cond
  8.1066-              #+gcl
  8.1067-              ((or (< system::*gcl-major-version* 2)
  8.1068-                   (and (= system::*gcl-major-version* 2)
  8.1069-                        (< system::*gcl-minor-version* 7)))
  8.1070-               '(error "GCL 2.7 or later required to use ASDF")))))
  8.1071-      (eval code)
  8.1072-      code))
  8.1073-
  8.1074-#+genera
  8.1075-(eval-when (:load-toplevel :compile-toplevel :execute)
  8.1076-  (unless (fboundp 'lambda)
  8.1077-    (defmacro lambda (&whole form &rest bvl-decls-and-body)
  8.1078-      (declare (ignore bvl-decls-and-body)(zwei::indentation 1 1))
  8.1079-      `#',(cons 'lisp::lambda (cdr form))))
  8.1080-  (unless (fboundp 'ensure-directories-exist)
  8.1081-    (defun ensure-directories-exist (path)
  8.1082-      (fs:create-directories-recursively (pathname path))))
  8.1083-  (unless (fboundp 'read-sequence)
  8.1084-    (defun read-sequence (sequence stream &key (start 0) end)
  8.1085-      (scl:send stream :string-in nil sequence start end)))
  8.1086-  (unless (fboundp 'write-sequence)
  8.1087-    (defun write-sequence (sequence stream &key (start 0) end)
  8.1088-      (scl:send stream :string-out sequence start end)
  8.1089-      sequence)))
  8.1090-
  8.1091-#+lispworks
  8.1092-(eval-when (:load-toplevel :compile-toplevel :execute)
  8.1093-  ;; lispworks 3 and earlier cannot be checked for so we always assume
  8.1094-  ;; at least version 4
  8.1095-  (unless (member :lispworks4 *features*)
  8.1096-    (pushnew :lispworks5+ *features*)
  8.1097-    (unless (member :lispworks5 *features*)
  8.1098-      (pushnew :lispworks6+ *features*)
  8.1099-      (unless (member :lispworks6 *features*)
  8.1100-        (pushnew :lispworks7+ *features*)))))
  8.1101-
  8.1102-
  8.1103-#.(or #+mcl ;; the #$ doesn't work on other lisps, even protected by #+mcl, so we use this trick
  8.1104-      (read-from-string
  8.1105-       "(eval-when (:load-toplevel :compile-toplevel :execute)
  8.1106-          (ccl:define-entry-point (_getenv \"getenv\") ((name :string)) :string)
  8.1107-          (ccl:define-entry-point (_system \"system\") ((name :string)) :int)
  8.1108-          ;; Note: ASDF may expect user-homedir-pathname to provide
  8.1109-          ;; the pathname of the current user's home directory, whereas
  8.1110-          ;; MCL by default provides the directory from which MCL was started.
  8.1111-          ;; See http://code.google.com/p/mcl/wiki/Portability
  8.1112-          (defun user-homedir-pathname ()
  8.1113-            (ccl::findfolder #$kuserdomain #$kCurrentUserFolderType))
  8.1114-          (defun probe-posix (posix-namestring)
  8.1115-            \"If a file exists for the posix namestring, return the pathname\"
  8.1116-            (ccl::with-cstrs ((cpath posix-namestring))
  8.1117-              (ccl::rlet ((is-dir :boolean)
  8.1118-                          (fsref :fsref))
  8.1119-                (when (eq #$noerr (#_fspathmakeref cpath fsref is-dir))
  8.1120-                  (ccl::%path-from-fsref fsref is-dir))))))"))
  8.1121-
  8.1122-#+mkcl
  8.1123-(eval-when (:load-toplevel :compile-toplevel :execute)
  8.1124-  (require :cmp)
  8.1125-  (setq clos::*redefine-class-in-place* t)) ;; Make sure we have strict ANSI class redefinition semantics
  8.1126-
  8.1127-
  8.1128-;;;; compatfmt: avoid fancy format directives when unsupported
  8.1129-(eval-when (:load-toplevel :compile-toplevel :execute)
  8.1130-  (defun frob-substrings (string substrings &optional frob)
  8.1131-    "for each substring in SUBSTRINGS, find occurrences of it within STRING
  8.1132-that don't use parts of matched occurrences of previous strings, and
  8.1133-FROB them, that is to say, remove them if FROB is NIL,
  8.1134-replace by FROB if FROB is a STRING, or if FROB is a FUNCTION,
  8.1135-call FROB with the match and a function that emits a string in the output.
  8.1136-Return a string made of the parts not omitted or emitted by FROB."
  8.1137-    (declare (optimize (speed 0) (safety #-gcl 3 #+gcl 0) (debug 3)))
  8.1138-    (let ((length (length string)) (stream nil))
  8.1139-      (labels ((emit-string (x &optional (start 0) (end (length x)))
  8.1140-                 (when (< start end)
  8.1141-                   (unless stream (setf stream (make-string-output-stream)))
  8.1142-                   (write-string x stream :start start :end end)))
  8.1143-               (emit-substring (start end)
  8.1144-                 (when (and (zerop start) (= end length))
  8.1145-                   (return-from frob-substrings string))
  8.1146-                 (emit-string string start end))
  8.1147-               (recurse (substrings start end)
  8.1148-                 (cond
  8.1149-                   ((>= start end))
  8.1150-                   ((null substrings) (emit-substring start end))
  8.1151-                   (t (let* ((sub-spec (first substrings))
  8.1152-                             (sub (if (consp sub-spec) (car sub-spec) sub-spec))
  8.1153-                             (fun (if (consp sub-spec) (cdr sub-spec) frob))
  8.1154-                             (found (search sub string :start2 start :end2 end))
  8.1155-                             (more (rest substrings)))
  8.1156-                        (cond
  8.1157-                          (found
  8.1158-                           (recurse more start found)
  8.1159-                           (etypecase fun
  8.1160-                             (null)
  8.1161-                             (string (emit-string fun))
  8.1162-                             (function (funcall fun sub #'emit-string)))
  8.1163-                           (recurse substrings (+ found (length sub)) end))
  8.1164-                          (t
  8.1165-                           (recurse more start end))))))))
  8.1166-        (recurse substrings 0 length))
  8.1167-      (if stream (get-output-stream-string stream) "")))
  8.1168-
  8.1169-  (defmacro compatfmt (format)
  8.1170-    #+(or gcl genera)
  8.1171-    (frob-substrings format `("~3i~_" #+genera ,@'("~@<" "~@;" "~@:>" "~:>")))
  8.1172-    #-(or gcl genera) format))
  8.1173-;;;; -------------------------------------------------------------------------
  8.1174-;;;; General Purpose Utilities for ASDF
  8.1175-
  8.1176-(uiop/package:define-package :uiop/utility
  8.1177-  (:use :uiop/common-lisp :uiop/package)
  8.1178-  ;; import and reexport a few things defined in :uiop/common-lisp
  8.1179-  (:import-from :uiop/common-lisp #:compatfmt #:frob-substrings
  8.1180-   #+(or clasp ecl) #:use-ecl-byte-compiler-p #+mcl #:probe-posix)
  8.1181-  (:export #:compatfmt #:frob-substrings #:compatfmt
  8.1182-   #+(or clasp ecl) #:use-ecl-byte-compiler-p #+mcl #:probe-posix)
  8.1183-  (:export
  8.1184-   ;; magic helper to define debugging functions:
  8.1185-   #:uiop-debug #:load-uiop-debug-utility #:*uiop-debug-utility*
  8.1186-   #:with-upgradability ;; (un)defining functions in an upgrade-friendly way
  8.1187-   #:nest #:if-let ;; basic flow control
  8.1188-   #:parse-body ;; macro definition helper
  8.1189-   #:while-collecting #:appendf #:length=n-p #:ensure-list ;; lists
  8.1190-   #:remove-plist-keys #:remove-plist-key ;; plists
  8.1191-   #:emptyp ;; sequences
  8.1192-   #:+non-base-chars-exist-p+ ;; characters
  8.1193-   #:+max-character-type-index+ #:character-type-index #:+character-types+
  8.1194-   #:base-string-p #:strings-common-element-type #:reduce/strcat #:strcat ;; strings
  8.1195-   #:first-char #:last-char #:split-string #:stripln #:+cr+ #:+lf+ #:+crlf+
  8.1196-   #:string-prefix-p #:string-enclosed-p #:string-suffix-p
  8.1197-   #:standard-case-symbol-name #:find-standard-case-symbol ;; symbols
  8.1198-   #:coerce-class ;; CLOS
  8.1199-   #:timestamp< #:timestamps< #:timestamp*< #:timestamp<= ;; timestamps
  8.1200-   #:earlier-timestamp #:timestamps-earliest #:earliest-timestamp
  8.1201-   #:later-timestamp #:timestamps-latest #:latest-timestamp #:latest-timestamp-f
  8.1202-   #:list-to-hash-set #:ensure-gethash ;; hash-table
  8.1203-   #:ensure-function #:access-at #:access-at-count ;; functions
  8.1204-   #:call-function #:call-functions #:register-hook-function
  8.1205-   #:lexicographic< #:lexicographic<= ;; version
  8.1206-   #:simple-style-warning #:style-warn ;; simple style warnings
  8.1207-   #:match-condition-p #:match-any-condition-p ;; conditions
  8.1208-   #:call-with-muffled-conditions #:with-muffled-conditions
  8.1209-   #:not-implemented-error #:parameter-error
  8.1210-   #:symbol-test-to-feature-expression
  8.1211-   #:boolean-to-feature-expression))
  8.1212-(in-package :uiop/utility)
  8.1213-
  8.1214-;;;; Defining functions in a way compatible with hot-upgrade:
  8.1215-;; - The WTIH-UPGRADABILITY infrastructure below ensures that functions are declared NOTINLINE,
  8.1216-;;   so that new definitions are always seen by all callers, even those up the stack.
  8.1217-;; - WITH-UPGRADABILITY also uses EVAL-WHEN so that definitions used by ASDF are in a limbo state
  8.1218-;;   (especially for gf's) in between the COMPILE-OP and LOAD-OP operations on the defining file.
  8.1219-;; - THOU SHALT NOT redefine a function with a backward-incompatible semantics without renaming it,
  8.1220-;;   at least if that function is used by ASDF while performing the plan to load ASDF.
  8.1221-;; - THOU SHALT change the name of a function whenever thou makest an incompatible change.
  8.1222-;; - For instance, when the meanings of NIL and T for timestamps was inverted,
  8.1223-;;   functions in the STAMP<, STAMP<=, etc. family had to be renamed to TIMESTAMP<, TIMESTAMP<=, etc.,
  8.1224-;;   because the change other caused a huge incompatibility during upgrade.
  8.1225-;; - Whenever a function goes from a DEFUN to a DEFGENERIC, or the DEFGENERIC signature changes, etc.,
  8.1226-;;   even in a backward-compatible way, you MUST precede the definition by FMAKUNBOUND.
  8.1227-;; - Since FMAKUNBOUND will remove all the methods on the generic function, make sure that
  8.1228-;;   all the methods required for ASDF to successfully continue compiling itself
  8.1229-;;   shall be defined in the same file as the one with the FMAKUNBOUND, *after* the DEFGENERIC.
  8.1230-;; - When a function goes from DEFGENERIC to DEFUN, you may omit to use FMAKUNBOUND.
  8.1231-;; - For safety, you shall put the FMAKUNBOUND just before the DEFUN or DEFGENERIC,
  8.1232-;;   in the same WITH-UPGRADABILITY form (and its implicit EVAL-WHEN).
  8.1233-;; - Any time you change a signature, please keep a comment specifying the first release after the change;
  8.1234-;;   put that comment on the same line as FMAKUNBOUND, it you use FMAKUNBOUND.
  8.1235-(eval-when (:load-toplevel :compile-toplevel :execute)
  8.1236-  (defun ensure-function-notinline (definition &aux (name (second definition)))
  8.1237-    (assert (member (first definition) '(defun defgeneric)))
  8.1238-    `(progn
  8.1239-       ,(when (and #+(or clasp ecl) (symbolp name)) ; NB: fails for (SETF functions) on ECL
  8.1240-          `(declaim (notinline ,name)))
  8.1241-       ,definition))
  8.1242-  (defmacro with-upgradability ((&optional) &body body)
  8.1243-    "Evaluate BODY at compile- load- and run- times, with DEFUN and DEFGENERIC modified
  8.1244-to also declare the functions NOTINLINE and to accept a wrapping the function name
  8.1245-specification into a list with keyword argument SUPERSEDE (which defaults to T if the name
  8.1246-is not wrapped, and NIL if it is wrapped). If SUPERSEDE is true, call UNDEFINE-FUNCTION
  8.1247-to supersede any previous definition."
  8.1248-    `(eval-when (:compile-toplevel :load-toplevel :execute)
  8.1249-       ,@(loop :for form :in body :collect
  8.1250-               (if (consp form)
  8.1251-                   (case (first form)
  8.1252-                     ((defun defgeneric) (ensure-function-notinline form))
  8.1253-                     (otherwise form))
  8.1254-                   form)))))
  8.1255-
  8.1256-;;; Magic debugging help. See contrib/debug.lisp
  8.1257-(with-upgradability ()
  8.1258-  (defvar *uiop-debug-utility*
  8.1259-    '(symbol-call :uiop :subpathname (symbol-call :uiop :uiop-directory) "contrib/debug.lisp")
  8.1260-    "form that evaluates to the pathname to your favorite debugging utilities")
  8.1261-
  8.1262-  (defmacro uiop-debug (&rest keys)
  8.1263-    "Load the UIOP debug utility at compile-time as well as runtime"
  8.1264-    `(eval-when (:compile-toplevel :load-toplevel :execute)
  8.1265-       (load-uiop-debug-utility ,@keys)))
  8.1266-
  8.1267-  (defun load-uiop-debug-utility (&key package utility-file)
  8.1268-    "Load the UIOP debug utility in given PACKAGE (default *PACKAGE*).
  8.1269-Beware: The utility is located by EVAL'uating the UTILITY-FILE form (default *UIOP-DEBUG-UTILITY*)."
  8.1270-    (let* ((*package* (if package (find-package package) *package*))
  8.1271-           (keyword (read-from-string
  8.1272-                     (format nil ":DBG-~:@(~A~)" (package-name *package*)))))
  8.1273-      (unless (member keyword *features*)
  8.1274-        (let* ((utility-file (or utility-file *uiop-debug-utility*))
  8.1275-               (file (ignore-errors (probe-file (eval utility-file)))))
  8.1276-          (if file (load file)
  8.1277-              (error "Failed to locate debug utility file: ~S" utility-file)))))))
  8.1278-
  8.1279-;;; Flow control
  8.1280-(with-upgradability ()
  8.1281-  (defmacro nest (&rest things)
  8.1282-    "Macro to keep code nesting and indentation under control." ;; Thanks to mbaringer
  8.1283-    (reduce #'(lambda (outer inner) `(,@outer ,inner))
  8.1284-            things :from-end t))
  8.1285-
  8.1286-  (defmacro if-let (bindings &body (then-form &optional else-form)) ;; from alexandria
  8.1287-    ;; bindings can be (var form) or ((var1 form1) ...)
  8.1288-    (let* ((binding-list (if (and (consp bindings) (symbolp (car bindings)))
  8.1289-                             (list bindings)
  8.1290-                             bindings))
  8.1291-           (variables (mapcar #'car binding-list)))
  8.1292-      `(let ,binding-list
  8.1293-         (if (and ,@variables)
  8.1294-             ,then-form
  8.1295-             ,else-form)))))
  8.1296-
  8.1297-;;; Macro definition helper
  8.1298-(with-upgradability ()
  8.1299-  (defun parse-body (body &key documentation whole) ;; from alexandria
  8.1300-    "Parses BODY into (values remaining-forms declarations doc-string).
  8.1301-Documentation strings are recognized only if DOCUMENTATION is true.
  8.1302-Syntax errors in body are signalled and WHOLE is used in the signal
  8.1303-arguments when given."
  8.1304-    (let ((doc nil)
  8.1305-          (decls nil)
  8.1306-          (current nil))
  8.1307-      (tagbody
  8.1308-       :declarations
  8.1309-         (setf current (car body))
  8.1310-         (when (and documentation (stringp current) (cdr body))
  8.1311-           (if doc
  8.1312-               (error "Too many documentation strings in ~S." (or whole body))
  8.1313-               (setf doc (pop body)))
  8.1314-           (go :declarations))
  8.1315-         (when (and (listp current) (eql (first current) 'declare))
  8.1316-           (push (pop body) decls)
  8.1317-           (go :declarations)))
  8.1318-      (values body (nreverse decls) doc))))
  8.1319-
  8.1320-
  8.1321-;;; List manipulation
  8.1322-(with-upgradability ()
  8.1323-  (defmacro while-collecting ((&rest collectors) &body body)
  8.1324-    "COLLECTORS should be a list of names for collections.  A collector
  8.1325-defines a function that, when applied to an argument inside BODY, will
  8.1326-add its argument to the corresponding collection.  Returns multiple values,
  8.1327-a list for each collection, in order.
  8.1328-   E.g.,
  8.1329-\(while-collecting \(foo bar\)
  8.1330-           \(dolist \(x '\(\(a 1\) \(b 2\) \(c 3\)\)\)
  8.1331-             \(foo \(first x\)\)
  8.1332-             \(bar \(second x\)\)\)\)
  8.1333-Returns two values: \(A B C\) and \(1 2 3\)."
  8.1334-    (let ((vars (mapcar #'(lambda (x) (gensym (symbol-name x))) collectors))
  8.1335-          (initial-values (mapcar (constantly nil) collectors)))
  8.1336-      `(let ,(mapcar #'list vars initial-values)
  8.1337-         (flet ,(mapcar #'(lambda (c v) `(,c (x) (push x ,v) (values))) collectors vars)
  8.1338-           ,@body
  8.1339-           (values ,@(mapcar #'(lambda (v) `(reverse ,v)) vars))))))
  8.1340-
  8.1341-  (define-modify-macro appendf (&rest args)
  8.1342-    append "Append onto list") ;; only to be used on short lists.
  8.1343-
  8.1344-  (defun length=n-p (x n) ;is it that (= (length x) n) ?
  8.1345-    (check-type n (integer 0 *))
  8.1346-    (loop
  8.1347-      :for l = x :then (cdr l)
  8.1348-      :for i :downfrom n :do
  8.1349-        (cond
  8.1350-          ((zerop i) (return (null l)))
  8.1351-          ((not (consp l)) (return nil)))))
  8.1352-
  8.1353-  (defun ensure-list (x)
  8.1354-    (if (listp x) x (list x))))
  8.1355-
  8.1356-
  8.1357-;;; Remove a key from a plist, i.e. for keyword argument cleanup
  8.1358-(with-upgradability ()
  8.1359-  (defun remove-plist-key (key plist)
  8.1360-    "Remove a single key from a plist"
  8.1361-    (loop :for (k v) :on plist :by #'cddr
  8.1362-          :unless (eq k key)
  8.1363-            :append (list k v)))
  8.1364-
  8.1365-  (defun remove-plist-keys (keys plist)
  8.1366-    "Remove a list of keys from a plist"
  8.1367-    (loop :for (k v) :on plist :by #'cddr
  8.1368-          :unless (member k keys)
  8.1369-            :append (list k v))))
  8.1370-
  8.1371-
  8.1372-;;; Sequences
  8.1373-(with-upgradability ()
  8.1374-  (defun emptyp (x)
  8.1375-    "Predicate that is true for an empty sequence"
  8.1376-    (or (null x) (and (vectorp x) (zerop (length x))))))
  8.1377-
  8.1378-
  8.1379-;;; Characters
  8.1380-(with-upgradability ()
  8.1381-  ;; base-char != character on ECL, LW, SBCL, Genera.
  8.1382-  ;; NB: We assume a total order on character types.
  8.1383-  ;; If that's not true... this code will need to be updated.
  8.1384-  (defparameter +character-types+ ;; assuming a simple hierarchy
  8.1385-    #.(coerce (loop :for (type next) :on
  8.1386-                    '(;; In SCL, all characters seem to be 16-bit base-char
  8.1387-                      ;; Yet somehow character fails to be a subtype of base-char
  8.1388-                      #-scl base-char
  8.1389-                      ;; LW6 has BASE-CHAR < SIMPLE-CHAR < CHARACTER
  8.1390-                      ;; LW7 has BASE-CHAR < BMP-CHAR < SIMPLE-CHAR = CHARACTER
  8.1391-                      #+lispworks7+ lw:bmp-char
  8.1392-                      #+lispworks lw:simple-char
  8.1393-                      character)
  8.1394-                    :unless (and next (subtypep next type))
  8.1395-                      :collect type) 'vector))
  8.1396-  (defparameter +max-character-type-index+ (1- (length +character-types+)))
  8.1397-  (defconstant +non-base-chars-exist-p+ (plusp +max-character-type-index+))
  8.1398-  (when +non-base-chars-exist-p+ (pushnew :non-base-chars-exist-p *features*)))
  8.1399-
  8.1400-(with-upgradability ()
  8.1401-  (defun character-type-index (x)
  8.1402-    (declare (ignorable x))
  8.1403-    #.(case +max-character-type-index+
  8.1404-        (0 0)
  8.1405-        (1 '(etypecase x
  8.1406-             (character (if (typep x 'base-char) 0 1))
  8.1407-             (symbol (if (subtypep x 'base-char) 0 1))))
  8.1408-        (otherwise
  8.1409-         '(or (position-if (etypecase x
  8.1410-                             (character #'(lambda (type) (typep x type)))
  8.1411-                             (symbol #'(lambda (type) (subtypep x type))))
  8.1412-               +character-types+)
  8.1413-           (error "Not a character or character type: ~S" x))))))
  8.1414-
  8.1415-
  8.1416-;;; Strings
  8.1417-(with-upgradability ()
  8.1418-  (defun base-string-p (string)
  8.1419-    "Does the STRING only contain BASE-CHARs?"
  8.1420-    (declare (ignorable string))
  8.1421-    (and #+non-base-chars-exist-p (eq 'base-char (array-element-type string))))
  8.1422-
  8.1423-  (defun strings-common-element-type (strings)
  8.1424-    "What least subtype of CHARACTER can contain all the elements of all the STRINGS?"
  8.1425-    (declare (ignorable strings))
  8.1426-    #.(if +non-base-chars-exist-p+
  8.1427-          `(aref +character-types+
  8.1428-            (loop :with index = 0 :for s :in strings :do
  8.1429-              (flet ((consider (i)
  8.1430-                       (cond ((= i ,+max-character-type-index+) (return i))
  8.1431-                             ,@(when (> +max-character-type-index+ 1) `(((> i index) (setf index i)))))))
  8.1432-                (cond
  8.1433-                  ((emptyp s)) ;; NIL or empty string
  8.1434-                  ((characterp s) (consider (character-type-index s)))
  8.1435-                  ((stringp s) (let ((string-type-index
  8.1436-                                       (character-type-index (array-element-type s))))
  8.1437-                                 (unless (>= index string-type-index)
  8.1438-                                   (loop :for c :across s :for i = (character-type-index c)
  8.1439-                                         :do (consider i)
  8.1440-                                         ,@(when (> +max-character-type-index+ 1)
  8.1441-                                             `((when (= i string-type-index) (return))))))))
  8.1442-                  (t (error "Invalid string designator ~S for ~S" s 'strings-common-element-type))))
  8.1443-                  :finally (return index)))
  8.1444-          ''character))
  8.1445-
  8.1446-  (defun reduce/strcat (strings &key key start end)
  8.1447-    "Reduce a list as if by STRCAT, accepting KEY START and END keywords like REDUCE.
  8.1448-NIL is interpreted as an empty string. A character is interpreted as a string of length one."
  8.1449-    (when (or start end) (setf strings (subseq strings start end)))
  8.1450-    (when key (setf strings (mapcar key strings)))
  8.1451-    (loop :with output = (make-string (loop :for s :in strings
  8.1452-                                            :sum (if (characterp s) 1 (length s)))
  8.1453-                                      :element-type (strings-common-element-type strings))
  8.1454-          :with pos = 0
  8.1455-          :for input :in strings
  8.1456-          :do (etypecase input
  8.1457-                (null)
  8.1458-                (character (setf (char output pos) input) (incf pos))
  8.1459-                (string (replace output input :start1 pos) (incf pos (length input))))
  8.1460-          :finally (return output)))
  8.1461-
  8.1462-  (defun strcat (&rest strings)
  8.1463-    "Concatenate strings.
  8.1464-NIL is interpreted as an empty string, a character as a string of length one."
  8.1465-    (reduce/strcat strings))
  8.1466-
  8.1467-  (defun first-char (s)
  8.1468-    "Return the first character of a non-empty string S, or NIL"
  8.1469-    (and (stringp s) (plusp (length s)) (char s 0)))
  8.1470-
  8.1471-  (defun last-char (s)
  8.1472-    "Return the last character of a non-empty string S, or NIL"
  8.1473-    (and (stringp s) (plusp (length s)) (char s (1- (length s)))))
  8.1474-
  8.1475-  (defun split-string (string &key max (separator '(#\Space #\Tab)))
  8.1476-    "Split STRING into a list of components separated by
  8.1477-any of the characters in the sequence SEPARATOR.
  8.1478-If MAX is specified, then no more than max(1,MAX) components will be returned,
  8.1479-starting the separation from the end, e.g. when called with arguments
  8.1480- \"a.b.c.d.e\" :max 3 :separator \".\" it will return (\"a.b.c\" \"d\" \"e\")."
  8.1481-    (block ()
  8.1482-      (let ((list nil) (words 0) (end (length string)))
  8.1483-        (when (zerop end) (return nil))
  8.1484-        (flet ((separatorp (char) (find char separator))
  8.1485-               (done () (return (cons (subseq string 0 end) list))))
  8.1486-          (loop
  8.1487-            :for start = (if (and max (>= words (1- max)))
  8.1488-                             (done)
  8.1489-                             (position-if #'separatorp string :end end :from-end t))
  8.1490-            :do (when (null start) (done))
  8.1491-                (push (subseq string (1+ start) end) list)
  8.1492-                (incf words)
  8.1493-                (setf end start))))))
  8.1494-
  8.1495-  (defun string-prefix-p (prefix string)
  8.1496-    "Does STRING begin with PREFIX?"
  8.1497-    (let* ((x (string prefix))
  8.1498-           (y (string string))
  8.1499-           (lx (length x))
  8.1500-           (ly (length y)))
  8.1501-      (and (<= lx ly) (string= x y :end2 lx))))
  8.1502-
  8.1503-  (defun string-suffix-p (string suffix)
  8.1504-    "Does STRING end with SUFFIX?"
  8.1505-    (let* ((x (string string))
  8.1506-           (y (string suffix))
  8.1507-           (lx (length x))
  8.1508-           (ly (length y)))
  8.1509-      (and (<= ly lx) (string= x y :start1 (- lx ly)))))
  8.1510-
  8.1511-  (defun string-enclosed-p (prefix string suffix)
  8.1512-    "Does STRING begin with PREFIX and end with SUFFIX?"
  8.1513-    (and (string-prefix-p prefix string)
  8.1514-         (string-suffix-p string suffix)))
  8.1515-
  8.1516-  (defvar +cr+ (coerce #(#\Return) 'string))
  8.1517-  (defvar +lf+ (coerce #(#\Linefeed) 'string))
  8.1518-  (defvar +crlf+ (coerce #(#\Return #\Linefeed) 'string))
  8.1519-
  8.1520-  (defun stripln (x)
  8.1521-    "Strip a string X from any ending CR, LF or CRLF.
  8.1522-Return two values, the stripped string and the ending that was stripped,
  8.1523-or the original value and NIL if no stripping took place.
  8.1524-Since our STRCAT accepts NIL as empty string designator,
  8.1525-the two results passed to STRCAT always reconstitute the original string"
  8.1526-    (check-type x string)
  8.1527-    (block nil
  8.1528-      (flet ((c (end) (when (string-suffix-p x end)
  8.1529-                        (return (values (subseq x 0 (- (length x) (length end))) end)))))
  8.1530-        (when x (c +crlf+) (c +lf+) (c +cr+) (values x nil)))))
  8.1531-
  8.1532-  (defun standard-case-symbol-name (name-designator)
  8.1533-    "Given a NAME-DESIGNATOR for a symbol, if it is a symbol, convert it to a string using STRING;
  8.1534-if it is a string, use STRING-UPCASE on an ANSI CL platform, or STRING on a so-called \"modern\"
  8.1535-platform such as Allegro with modern syntax."
  8.1536-    (check-type name-designator (or string symbol))
  8.1537-    (cond
  8.1538-      ((or (symbolp name-designator) #+allegro (eq excl:*current-case-mode* :case-sensitive-lower))
  8.1539-       (string name-designator))
  8.1540-      ;; Should we be doing something on CLISP?
  8.1541-      (t (string-upcase name-designator))))
  8.1542-
  8.1543-  (defun find-standard-case-symbol (name-designator package-designator &optional (error t))
  8.1544-    "Find a symbol designated by NAME-DESIGNATOR in a package designated by PACKAGE-DESIGNATOR,
  8.1545-where STANDARD-CASE-SYMBOL-NAME is used to transform them if these designators are strings.
  8.1546-If optional ERROR argument is NIL, return NIL instead of an error when the symbol is not found."
  8.1547-    (find-symbol* (standard-case-symbol-name name-designator)
  8.1548-                  (etypecase package-designator
  8.1549-                    ((or package symbol) package-designator)
  8.1550-                    (string (standard-case-symbol-name package-designator)))
  8.1551-                  error)))
  8.1552-
  8.1553-;;; timestamps: a REAL or a boolean where T=-infinity, NIL=+infinity
  8.1554-(eval-when (#-lispworks :compile-toplevel :load-toplevel :execute)
  8.1555-  (deftype timestamp () '(or real boolean)))
  8.1556-(with-upgradability ()
  8.1557-  (defun timestamp< (x y)
  8.1558-    (etypecase x
  8.1559-      ((eql t) (not (eql y t)))
  8.1560-      (real (etypecase y
  8.1561-              ((eql t) nil)
  8.1562-              (real (< x y))
  8.1563-              (null t)))
  8.1564-      (null nil)))
  8.1565-  (defun timestamps< (list) (loop :for y :in list :for x = nil :then y :always (timestamp< x y)))
  8.1566-  (defun timestamp*< (&rest list) (timestamps< list))
  8.1567-  (defun timestamp<= (x y) (not (timestamp< y x)))
  8.1568-  (defun earlier-timestamp (x y) (if (timestamp< x y) x y))
  8.1569-  (defun timestamps-earliest (list) (reduce 'earlier-timestamp list :initial-value nil))
  8.1570-  (defun earliest-timestamp (&rest list) (timestamps-earliest list))
  8.1571-  (defun later-timestamp (x y) (if (timestamp< x y) y x))
  8.1572-  (defun timestamps-latest (list) (reduce 'later-timestamp list :initial-value t))
  8.1573-  (defun latest-timestamp (&rest list) (timestamps-latest list))
  8.1574-  (define-modify-macro latest-timestamp-f (&rest timestamps) latest-timestamp))
  8.1575-
  8.1576-
  8.1577-;;; Function designators
  8.1578-(with-upgradability ()
  8.1579-  (defun ensure-function (fun &key (package :cl))
  8.1580-    "Coerce the object FUN into a function.
  8.1581-
  8.1582-If FUN is a FUNCTION, return it.
  8.1583-If the FUN is a non-sequence literal constant, return constantly that,
  8.1584-i.e. for a boolean keyword character number or pathname.
  8.1585-Otherwise if FUN is a non-literally constant symbol, return its FDEFINITION.
  8.1586-If FUN is a CONS, return the function that applies its CAR
  8.1587-to the appended list of the rest of its CDR and the arguments,
  8.1588-unless the CAR is LAMBDA, in which case the expression is evaluated.
  8.1589-If FUN is a string, READ a form from it in the specified PACKAGE (default: CL)
  8.1590-and EVAL that in a (FUNCTION ...) context."
  8.1591-    (etypecase fun
  8.1592-      (function fun)
  8.1593-      ((or boolean keyword character number pathname) (constantly fun))
  8.1594-      (hash-table #'(lambda (x) (gethash x fun)))
  8.1595-      (symbol (fdefinition fun))
  8.1596-      (cons (if (eq 'lambda (car fun))
  8.1597-                (eval fun)
  8.1598-                #'(lambda (&rest args) (apply (car fun) (append (cdr fun) args)))))
  8.1599-      (string (eval `(function ,(with-standard-io-syntax
  8.1600-                                  (let ((*package* (find-package package)))
  8.1601-                                    (read-from-string fun))))))))
  8.1602-
  8.1603-  (defun access-at (object at)
  8.1604-    "Given an OBJECT and an AT specifier, list of successive accessors,
  8.1605-call each accessor on the result of the previous calls.
  8.1606-An accessor may be an integer, meaning a call to ELT,
  8.1607-a keyword, meaning a call to GETF,
  8.1608-NIL, meaning identity,
  8.1609-a function or other symbol, meaning itself,
  8.1610-or a list of a function designator and arguments, interpreted as per ENSURE-FUNCTION.
  8.1611-As a degenerate case, the AT specifier may be an atom of a single such accessor
  8.1612-instead of a list."
  8.1613-    (flet ((access (object accessor)
  8.1614-             (etypecase accessor
  8.1615-               (function (funcall accessor object))
  8.1616-               (integer (elt object accessor))
  8.1617-               (keyword (getf object accessor))
  8.1618-               (null object)
  8.1619-               (symbol (funcall accessor object))
  8.1620-               (cons (funcall (ensure-function accessor) object)))))
  8.1621-      (if (listp at)
  8.1622-          (dolist (accessor at object)
  8.1623-            (setf object (access object accessor)))
  8.1624-          (access object at))))
  8.1625-
  8.1626-  (defun access-at-count (at)
  8.1627-    "From an AT specification, extract a COUNT of maximum number
  8.1628-of sub-objects to read as per ACCESS-AT"
  8.1629-    (cond
  8.1630-      ((integerp at)
  8.1631-       (1+ at))
  8.1632-      ((and (consp at) (integerp (first at)))
  8.1633-       (1+ (first at)))))
  8.1634-
  8.1635-  (defun call-function (function-spec &rest arguments)
  8.1636-    "Call the function designated by FUNCTION-SPEC as per ENSURE-FUNCTION,
  8.1637-with the given ARGUMENTS"
  8.1638-    (apply (ensure-function function-spec) arguments))
  8.1639-
  8.1640-  (defun call-functions (function-specs)
  8.1641-    "For each function in the list FUNCTION-SPECS, in order, call the function as per CALL-FUNCTION"
  8.1642-    (map () 'call-function function-specs))
  8.1643-
  8.1644-  (defun register-hook-function (variable hook &optional call-now-p)
  8.1645-    "Push the HOOK function (a designator as per ENSURE-FUNCTION) onto the hook VARIABLE.
  8.1646-When CALL-NOW-P is true, also call the function immediately."
  8.1647-    (pushnew hook (symbol-value variable) :test 'equal)
  8.1648-    (when call-now-p (call-function hook))))
  8.1649-
  8.1650-
  8.1651-;;; CLOS
  8.1652-(with-upgradability ()
  8.1653-  (defun coerce-class (class &key (package :cl) (super t) (error 'error))
  8.1654-    "Coerce CLASS to a class that is subclass of SUPER if specified,
  8.1655-or invoke ERROR handler as per CALL-FUNCTION.
  8.1656-
  8.1657-A keyword designates the name a symbol, which when found in either PACKAGE, designates a class.
  8.1658--- for backward compatibility, *PACKAGE* is also accepted for now, but this may go in the future.
  8.1659-A string is read as a symbol while in PACKAGE, the symbol designates a class.
  8.1660-
  8.1661-A class object designates itself.
  8.1662-NIL designates itself (no class).
  8.1663-A symbol otherwise designates a class by name."
  8.1664-    (let* ((normalized
  8.1665-            (typecase class
  8.1666-              (keyword (or (find-symbol* class package nil)
  8.1667-                           (find-symbol* class *package* nil)))
  8.1668-              (string (symbol-call :uiop :safe-read-from-string class :package package))
  8.1669-              (t class)))
  8.1670-           (found
  8.1671-            (etypecase normalized
  8.1672-              ((or standard-class built-in-class) normalized)
  8.1673-              ((or null keyword) nil)
  8.1674-              (symbol (find-class normalized nil nil))))
  8.1675-           (super-class
  8.1676-            (etypecase super
  8.1677-              ((or standard-class built-in-class) super)
  8.1678-              ((or null keyword) nil)
  8.1679-              (symbol (find-class super nil nil)))))
  8.1680-      #+allegro (when found (mop:finalize-inheritance found))
  8.1681-      (or (and found
  8.1682-               (or (eq super t) (#-cormanlisp subtypep #+cormanlisp cl::subclassp found super-class))
  8.1683-               found)
  8.1684-          (call-function error "Can't coerce ~S to a ~:[class~;subclass of ~:*~S~]" class super)))))
  8.1685-
  8.1686-
  8.1687-;;; Hash-tables
  8.1688-(with-upgradability ()
  8.1689-  (defun ensure-gethash (key table default)
  8.1690-    "Lookup the TABLE for a KEY as by GETHASH, but if not present,
  8.1691-call the (possibly constant) function designated by DEFAULT as per CALL-FUNCTION,
  8.1692-set the corresponding entry to the result in the table.
  8.1693-Return two values: the entry after its optional computation, and whether it was found"
  8.1694-    (multiple-value-bind (value foundp) (gethash key table)
  8.1695-      (values
  8.1696-       (if foundp
  8.1697-           value
  8.1698-           (setf (gethash key table) (call-function default)))
  8.1699-       foundp)))
  8.1700-
  8.1701-  (defun list-to-hash-set (list &aux (h (make-hash-table :test 'equal)))
  8.1702-    "Convert a LIST into hash-table that has the same elements when viewed as a set,
  8.1703-up to the given equality TEST"
  8.1704-    (dolist (x list h) (setf (gethash x h) t))))
  8.1705-
  8.1706-
  8.1707-;;; Lexicographic comparison of lists of numbers
  8.1708-(with-upgradability ()
  8.1709-  (defun lexicographic< (element< x y)
  8.1710-    "Lexicographically compare two lists of using the function element< to compare elements.
  8.1711-element< is a strict total order; the resulting order on X and Y will also be strict."
  8.1712-    (cond ((null y) nil)
  8.1713-          ((null x) t)
  8.1714-          ((funcall element< (car x) (car y)) t)
  8.1715-          ((funcall element< (car y) (car x)) nil)
  8.1716-          (t (lexicographic< element< (cdr x) (cdr y)))))
  8.1717-
  8.1718-  (defun lexicographic<= (element< x y)
  8.1719-    "Lexicographically compare two lists of using the function element< to compare elements.
  8.1720-element< is a strict total order; the resulting order on X and Y will be a non-strict total order."
  8.1721-    (not (lexicographic< element< y x))))
  8.1722-
  8.1723-
  8.1724-;;; Simple style warnings
  8.1725-(with-upgradability ()
  8.1726-  (define-condition simple-style-warning
  8.1727-      #+sbcl (sb-int:simple-style-warning) #-sbcl (simple-condition style-warning)
  8.1728-    ())
  8.1729-
  8.1730-  (defun style-warn (datum &rest arguments)
  8.1731-    (etypecase datum
  8.1732-      (string (warn (make-condition 'simple-style-warning :format-control datum :format-arguments arguments)))
  8.1733-      (symbol (assert (subtypep datum 'style-warning)) (apply 'warn datum arguments))
  8.1734-      (style-warning (apply 'warn datum arguments)))))
  8.1735-
  8.1736-
  8.1737-;;; Condition control
  8.1738-
  8.1739-(with-upgradability ()
  8.1740-  (defparameter +simple-condition-format-control-slot+
  8.1741-    #+abcl 'system::format-control
  8.1742-    #+allegro 'excl::format-control
  8.1743-    #+(or clasp ecl mkcl) 'si::format-control
  8.1744-    #+clisp 'system::$format-control
  8.1745-    #+clozure 'ccl::format-control
  8.1746-    #+(or cmucl scl) 'conditions::format-control
  8.1747-    #+(or gcl lispworks) 'conditions::format-string
  8.1748-    #+sbcl 'sb-kernel:format-control
  8.1749-    #-(or abcl allegro clasp clisp clozure cmucl ecl gcl lispworks mkcl sbcl scl) nil
  8.1750-    "Name of the slot for FORMAT-CONTROL in simple-condition")
  8.1751-
  8.1752-  (defun match-condition-p (x condition)
  8.1753-    "Compare received CONDITION to some pattern X:
  8.1754-a symbol naming a condition class,
  8.1755-a simple vector of length 2, arguments to find-symbol* with result as above,
  8.1756-or a string describing the format-control of a simple-condition."
  8.1757-    (etypecase x
  8.1758-      (symbol (typep condition x))
  8.1759-      ((simple-vector 2)
  8.1760-       (ignore-errors (typep condition (find-symbol* (svref x 0) (svref x 1) nil))))
  8.1761-      (function (funcall x condition))
  8.1762-      (string (and (typep condition 'simple-condition)
  8.1763-                   ;; On SBCL, it's always set and the check triggers a warning
  8.1764-                   #+(or allegro clozure cmucl lispworks scl)
  8.1765-                   (slot-boundp condition +simple-condition-format-control-slot+)
  8.1766-                   (ignore-errors (equal (simple-condition-format-control condition) x))))))
  8.1767-
  8.1768-  (defun match-any-condition-p (condition conditions)
  8.1769-    "match CONDITION against any of the patterns of CONDITIONS supplied"
  8.1770-    (loop :for x :in conditions :thereis (match-condition-p x condition)))
  8.1771-
  8.1772-  (defun call-with-muffled-conditions (thunk conditions)
  8.1773-    "calls the THUNK in a context where the CONDITIONS are muffled"
  8.1774-    (handler-bind ((t #'(lambda (c) (when (match-any-condition-p c conditions)
  8.1775-                                      (muffle-warning c)))))
  8.1776-      (funcall thunk)))
  8.1777-
  8.1778-  (defmacro with-muffled-conditions ((conditions) &body body)
  8.1779-    "Shorthand syntax for CALL-WITH-MUFFLED-CONDITIONS"
  8.1780-    `(call-with-muffled-conditions #'(lambda () ,@body) ,conditions)))
  8.1781-
  8.1782-;;; Conditions
  8.1783-
  8.1784-(with-upgradability ()
  8.1785-  (define-condition not-implemented-error (error)
  8.1786-    ((functionality :initarg :functionality)
  8.1787-     (format-control :initarg :format-control)
  8.1788-     (format-arguments :initarg :format-arguments))
  8.1789-    (:report (lambda (condition stream)
  8.1790-               (format stream "Not (currently) implemented on ~A: ~S~@[ ~?~]"
  8.1791-                       (nth-value 1 (symbol-call :uiop :implementation-type))
  8.1792-                       (slot-value condition 'functionality)
  8.1793-                       (slot-value condition 'format-control)
  8.1794-                       (slot-value condition 'format-arguments)))))
  8.1795-
  8.1796-  (defun not-implemented-error (functionality &optional format-control &rest format-arguments)
  8.1797-    "Signal an error because some FUNCTIONALITY is not implemented in the current version
  8.1798-of the software on the current platform; it may or may not be implemented in different combinations
  8.1799-of version of the software and of the underlying platform. Optionally, report a formatted error
  8.1800-message."
  8.1801-    (error 'not-implemented-error
  8.1802-           :functionality functionality
  8.1803-           :format-control format-control
  8.1804-           :format-arguments format-arguments))
  8.1805-
  8.1806-  (define-condition parameter-error (error)
  8.1807-    ((functionality :initarg :functionality)
  8.1808-     (format-control :initarg :format-control)
  8.1809-     (format-arguments :initarg :format-arguments))
  8.1810-    (:report (lambda (condition stream)
  8.1811-               (apply 'format stream
  8.1812-                       (slot-value condition 'format-control)
  8.1813-                       (slot-value condition 'functionality)
  8.1814-                       (slot-value condition 'format-arguments)))))
  8.1815-
  8.1816-  ;; Note that functionality MUST be passed as the second argument to parameter-error, just after
  8.1817-  ;; the format-control. If you want it to not appear in first position in actual message, use
  8.1818-  ;; ~* and ~:* to adjust parameter order.
  8.1819-  (defun parameter-error (format-control functionality &rest format-arguments)
  8.1820-    "Signal an error because some FUNCTIONALITY or its specific implementation on a given underlying
  8.1821-platform does not accept a given parameter or combination of parameters. Report a formatted error
  8.1822-message, that takes the functionality as its first argument (that can be skipped with ~*)."
  8.1823-    (error 'parameter-error
  8.1824-           :functionality functionality
  8.1825-           :format-control format-control
  8.1826-           :format-arguments format-arguments)))
  8.1827-
  8.1828-(with-upgradability ()
  8.1829-  (defun boolean-to-feature-expression (value)
  8.1830-    "Converts a boolean VALUE to a form suitable for testing with #+."
  8.1831-    (if value
  8.1832-        '(:and)
  8.1833-        '(:or)))
  8.1834-
  8.1835-  (defun symbol-test-to-feature-expression (name package)
  8.1836-    "Check if a symbol with a given NAME exists in PACKAGE and returns a
  8.1837-form suitable for testing with #+."
  8.1838-    (boolean-to-feature-expression
  8.1839-     (find-symbol* name package nil))))
  8.1840-(uiop/package:define-package :uiop/version
  8.1841-  (:recycle :uiop/version :uiop/utility :asdf)
  8.1842-  (:use :uiop/common-lisp :uiop/package :uiop/utility)
  8.1843-  (:export
  8.1844-   #:*uiop-version*
  8.1845-   #:parse-version #:unparse-version #:version< #:version<= #:version= ;; version support, moved from uiop/utility
  8.1846-   #:next-version
  8.1847-   #:deprecated-function-condition #:deprecated-function-name ;; deprecation control
  8.1848-   #:deprecated-function-style-warning #:deprecated-function-warning
  8.1849-   #:deprecated-function-error #:deprecated-function-should-be-deleted
  8.1850-   #:version-deprecation #:with-deprecation))
  8.1851-(in-package :uiop/version)
  8.1852-
  8.1853-(with-upgradability ()
  8.1854-  (defparameter *uiop-version* "3.3.6")
  8.1855-
  8.1856-  (defun unparse-version (version-list)
  8.1857-    "From a parsed version (a list of natural numbers), compute the version string"
  8.1858-    (format nil "~{~D~^.~}" version-list))
  8.1859-
  8.1860-  (defun parse-version (version-string &optional on-error)
  8.1861-    "Parse a VERSION-STRING as a series of natural numbers separated by dots.
  8.1862-Return a (non-null) list of integers if the string is valid;
  8.1863-otherwise return NIL.
  8.1864-
  8.1865-When invalid, ON-ERROR is called as per CALL-FUNCTION before to return NIL,
  8.1866-with format arguments explaining why the version is invalid.
  8.1867-ON-ERROR is also called if the version is not canonical
  8.1868-in that it doesn't print back to itself, but the list is returned anyway."
  8.1869-    (block nil
  8.1870-      (unless (stringp version-string)
  8.1871-        (call-function on-error "~S: ~S is not a string" 'parse-version version-string)
  8.1872-        (return))
  8.1873-      (unless (loop :for prev = nil :then c :for c :across version-string
  8.1874-                    :always (or (digit-char-p c)
  8.1875-                                (and (eql c #\.) prev (not (eql prev #\.))))
  8.1876-                    :finally (return (and c (digit-char-p c))))
  8.1877-        (call-function on-error "~S: ~S doesn't follow asdf version numbering convention"
  8.1878-                       'parse-version version-string)
  8.1879-        (return))
  8.1880-      (let* ((version-list
  8.1881-               (mapcar #'parse-integer (split-string version-string :separator ".")))
  8.1882-             (normalized-version (unparse-version version-list)))
  8.1883-        (unless (equal version-string normalized-version)
  8.1884-          (call-function on-error "~S: ~S contains leading zeros" 'parse-version version-string))
  8.1885-        version-list)))
  8.1886-
  8.1887-  (defun next-version (version)
  8.1888-    "When VERSION is not nil, it is a string, then parse it as a version, compute the next version
  8.1889-and return it as a string."
  8.1890-    (when version
  8.1891-      (let ((version-list (parse-version version)))
  8.1892-        (incf (car (last version-list)))
  8.1893-        (unparse-version version-list))))
  8.1894-
  8.1895-  (defun version< (version1 version2)
  8.1896-    "Given two version strings, return T if the second is strictly newer"
  8.1897-    (let ((v1 (parse-version version1 nil))
  8.1898-          (v2 (parse-version version2 nil)))
  8.1899-      (lexicographic< '< v1 v2)))
  8.1900-
  8.1901-  (defun version<= (version1 version2)
  8.1902-    "Given two version strings, return T if the second is newer or the same"
  8.1903-    (not (version< version2 version1))))
  8.1904-
  8.1905-  (defun version= (version1 version2)
  8.1906-    "Given two version strings, return T if the first is newer or the same and
  8.1907-the second is also newer or the same."
  8.1908-    (and (version<= version1 version2)
  8.1909-         (version<= version2 version1)))
  8.1910-
  8.1911-
  8.1912-(with-upgradability ()
  8.1913-  (define-condition deprecated-function-condition (condition)
  8.1914-    ((name :initarg :name :reader deprecated-function-name)))
  8.1915-  (define-condition deprecated-function-style-warning (deprecated-function-condition style-warning) ())
  8.1916-  (define-condition deprecated-function-warning (deprecated-function-condition warning) ())
  8.1917-  (define-condition deprecated-function-error (deprecated-function-condition error) ())
  8.1918-  (define-condition deprecated-function-should-be-deleted (deprecated-function-condition error) ())
  8.1919-
  8.1920-  (defun deprecated-function-condition-kind (type)
  8.1921-    (ecase type
  8.1922-      ((deprecated-function-style-warning) :style-warning)
  8.1923-      ((deprecated-function-warning) :warning)
  8.1924-      ((deprecated-function-error) :error)
  8.1925-      ((deprecated-function-should-be-deleted) :delete)))
  8.1926-
  8.1927-  (defmethod print-object ((c deprecated-function-condition) stream)
  8.1928-    (let ((name (deprecated-function-name c)))
  8.1929-      (cond
  8.1930-        (*print-readably*
  8.1931-         (let ((fmt "#.(make-condition '~S :name ~S)")
  8.1932-               (args (list (type-of c) name)))
  8.1933-           (if *read-eval*
  8.1934-               (apply 'format stream fmt args)
  8.1935-               (error "Can't print ~?" fmt args))))
  8.1936-        (*print-escape*
  8.1937-         (print-unreadable-object (c stream :type t) (format stream ":name ~S" name)))
  8.1938-        (t
  8.1939-         (let ((*package* (find-package :cl))
  8.1940-               (type (type-of c)))
  8.1941-           (format stream
  8.1942-                   (if (eq type 'deprecated-function-should-be-deleted)
  8.1943-                       "~A: Still defining deprecated function~:P ~{~S~^ ~} that promised to delete"
  8.1944-                       "~A: Using deprecated function ~S -- please update your code to use a newer API.~
  8.1945-~@[~%The docstring for this function says:~%~A~%~]")
  8.1946-                   type name (when (symbolp name) (documentation name 'function))))))))
  8.1947-
  8.1948-  (defun notify-deprecated-function (status name)
  8.1949-    (ecase status
  8.1950-      ((nil) nil)
  8.1951-      ((:style-warning) (style-warn 'deprecated-function-style-warning :name name))
  8.1952-      ((:warning) (warn 'deprecated-function-warning :name name))
  8.1953-      ((:error) (cerror "USE FUNCTION ANYWAY" 'deprecated-function-error :name name))))
  8.1954-
  8.1955-  (defun version-deprecation (version &key (style-warning nil)
  8.1956-                                        (warning (next-version style-warning))
  8.1957-                                        (error (next-version warning))
  8.1958-                                        (delete (next-version error)))
  8.1959-    "Given a VERSION string, and the starting versions for notifying the programmer of
  8.1960-various levels of deprecation, return the current level of deprecation as per WITH-DEPRECATION
  8.1961-that is the highest level that has a declared version older than the specified version.
  8.1962-Each start version for a level of deprecation can be specified by a keyword argument, or
  8.1963-if left unspecified, will be the NEXT-VERSION of the immediate lower level of deprecation."
  8.1964-    (cond
  8.1965-      ((and delete (version<= delete version)) :delete)
  8.1966-      ((and error (version<= error version)) :error)
  8.1967-      ((and warning (version<= warning version)) :warning)
  8.1968-      ((and style-warning (version<= style-warning version)) :style-warning)))
  8.1969-
  8.1970-  (defmacro with-deprecation ((level) &body definitions)
  8.1971-    "Given a deprecation LEVEL (a form to be EVAL'ed at macro-expansion time), instrument the
  8.1972-DEFUN and DEFMETHOD forms in DEFINITIONS to notify the programmer of the deprecation of the function
  8.1973-when it is compiled or called.
  8.1974-
  8.1975-Increasing levels (as result from evaluating LEVEL) are: NIL (not deprecated yet),
  8.1976-:STYLE-WARNING (a style warning is issued when used), :WARNING (a full warning is issued when used),
  8.1977-:ERROR (a continuable error instead), and :DELETE (it's an error if the code is still there while
  8.1978-at that level).
  8.1979-
  8.1980-Forms other than DEFUN and DEFMETHOD are not instrumented, and you can protect a DEFUN or DEFMETHOD
  8.1981-from instrumentation by enclosing it in a PROGN."
  8.1982-    (let ((level (eval level)))
  8.1983-      (check-type level (member nil :style-warning :warning :error :delete))
  8.1984-      (when (eq level :delete)
  8.1985-        (error 'deprecated-function-should-be-deleted :name
  8.1986-               (mapcar 'second
  8.1987-                       (remove-if-not #'(lambda (x) (member x '(defun defmethod)))
  8.1988-                                      definitions :key 'first))))
  8.1989-      (labels ((instrument (name head body whole)
  8.1990-                 (if level
  8.1991-                     (let ((notifiedp
  8.1992-                            (intern (format nil "*~A-~A-~A-~A*"
  8.1993-                                            :deprecated-function level name :notified-p))))
  8.1994-                       (multiple-value-bind (remaining-forms declarations doc-string)
  8.1995-                           (parse-body body :documentation t :whole whole)
  8.1996-                         `(progn
  8.1997-                            (defparameter ,notifiedp nil)
  8.1998-                            ;; tell some implementations to use the compiler-macro
  8.1999-                            (declaim (inline ,name))
  8.2000-                            (define-compiler-macro ,name (&whole form &rest args)
  8.2001-                              (declare (ignore args))
  8.2002-                              (notify-deprecated-function ,level ',name)
  8.2003-                              form)
  8.2004-                            (,@head ,@(when doc-string (list doc-string)) ,@declarations
  8.2005-                                    (unless ,notifiedp
  8.2006-                                      (setf ,notifiedp t)
  8.2007-                                      (notify-deprecated-function ,level ',name))
  8.2008-                                    ,@remaining-forms))))
  8.2009-                     `(progn
  8.2010-                        (eval-when (:compile-toplevel :load-toplevel :execute)
  8.2011-                          (setf (compiler-macro-function ',name) nil))
  8.2012-                        (declaim (notinline ,name))
  8.2013-                        (,@head ,@body)))))
  8.2014-        `(progn
  8.2015-           ,@(loop :for form :in definitions :collect
  8.2016-               (cond
  8.2017-                 ((and (consp form) (eq (car form) 'defun))
  8.2018-                  (instrument (second form) (subseq form 0 3) (subseq form 3) form))
  8.2019-                 ((and (consp form) (eq (car form) 'defmethod))
  8.2020-                  (let ((body-start (if (listp (third form)) 3 4)))
  8.2021-                    (instrument (second form)
  8.2022-                                (subseq form 0 body-start)
  8.2023-                                (subseq form body-start)
  8.2024-                                form)))
  8.2025-                 (t
  8.2026-                  form))))))))
  8.2027-;;;; ---------------------------------------------------------------------------
  8.2028-;;;; Access to the Operating System
  8.2029-
  8.2030-(uiop/package:define-package :uiop/os
  8.2031-  (:use :uiop/common-lisp :uiop/package :uiop/utility)
  8.2032-  (:export
  8.2033-   #:featurep #:os-unix-p #:os-macosx-p #:os-windows-p #:os-genera-p #:detect-os ;; features
  8.2034-   #:os-cond
  8.2035-   #:getenv #:getenvp ;; environment variables
  8.2036-   #:implementation-identifier ;; implementation identifier
  8.2037-   #:implementation-type #:*implementation-type*
  8.2038-   #:operating-system #:architecture #:lisp-version-string
  8.2039-   #:hostname #:getcwd #:chdir
  8.2040-   ;; Windows shortcut support
  8.2041-   #:read-null-terminated-string #:read-little-endian
  8.2042-   #:parse-file-location-info #:parse-windows-shortcut))
  8.2043-(in-package :uiop/os)
  8.2044-
  8.2045-;;; Features
  8.2046-(with-upgradability ()
  8.2047-  (defun featurep (x &optional (*features* *features*))
  8.2048-    "Checks whether a feature expression X is true with respect to the *FEATURES* set,
  8.2049-as per the CLHS standard for #+ and #-. Beware that just like the CLHS,
  8.2050-we assume symbols from the KEYWORD package are used, but that unless you're using #+/#-
  8.2051-your reader will not have magically used the KEYWORD package, so you need specify
  8.2052-keywords explicitly."
  8.2053-    (cond
  8.2054-      ((atom x) (and (member x *features*) t))
  8.2055-      ((eq :not (car x)) (assert (null (cddr x))) (not (featurep (cadr x))))
  8.2056-      ((eq :or (car x)) (some #'featurep (cdr x)))
  8.2057-      ((eq :and (car x)) (every #'featurep (cdr x)))
  8.2058-      (t (parameter-error "~S: malformed feature specification ~S" 'featurep x))))
  8.2059-
  8.2060-  ;; Starting with UIOP 3.1.5, these are runtime tests.
  8.2061-  ;; You may bind *features* with a copy of what your target system offers to test its properties.
  8.2062-  (defun os-macosx-p ()
  8.2063-    "Is the underlying operating system MacOS X?"
  8.2064-    ;; OS-MACOSX is not mutually exclusive with OS-UNIX,
  8.2065-    ;; in fact the former implies the latter.
  8.2066-    (featurep '(:or :darwin (:and :allegro :macosx) (:and :clisp :macos))))
  8.2067-
  8.2068-  (defun os-unix-p ()
  8.2069-    "Is the underlying operating system some Unix variant?"
  8.2070-    (or (featurep '(:or :unix :cygwin :haiku)) (os-macosx-p)))
  8.2071-
  8.2072-  (defun os-windows-p ()
  8.2073-    "Is the underlying operating system Microsoft Windows?"
  8.2074-    (and (not (os-unix-p)) (featurep '(:or :win32 :windows :mswindows :mingw32 :mingw64))))
  8.2075-
  8.2076-  (defun os-genera-p ()
  8.2077-    "Is the underlying operating system Genera (running on a Symbolics Lisp Machine)?"
  8.2078-    (featurep :genera))
  8.2079-
  8.2080-  (defun os-oldmac-p ()
  8.2081-    "Is the underlying operating system an (emulated?) MacOS 9 or earlier?"
  8.2082-    (featurep :mcl))
  8.2083-
  8.2084-  (defun os-haiku-p ()
  8.2085-    "Is the underlying operating system Haiku?"
  8.2086-    (featurep :haiku))
  8.2087-
  8.2088-  (defun os-mezzano-p ()
  8.2089-    "Is the underlying operating system Mezzano?"
  8.2090-    (featurep :mezzano))
  8.2091-
  8.2092-  (defun detect-os ()
  8.2093-    "Detects the current operating system. Only needs be run at compile-time,
  8.2094-except on ABCL where it might change between FASL compilation and runtime."
  8.2095-    (loop :with o
  8.2096-          :for (feature . detect) :in '((:os-unix . os-unix-p) (:os-macosx . os-macosx-p)
  8.2097-                                        (:os-windows . os-windows-p)
  8.2098-                                        (:os-genera . os-genera-p) (:os-oldmac . os-oldmac-p)
  8.2099-                                        (:os-haiku . os-haiku-p)
  8.2100-                                        (:os-mezzano . os-mezzano-p))
  8.2101-          :when (and (or (not o) (eq feature :os-macosx) (eq feature :os-haiku)) (funcall detect))
  8.2102-            :do (setf o feature) (pushnew feature *features*)
  8.2103-          :else :do (setf *features* (remove feature *features*))
  8.2104-          :finally
  8.2105-             (return (or o (error "Congratulations for trying ASDF on an operating system~%~
  8.2106-that is neither Unix, nor Windows, nor Genera, nor even old MacOS.~%Now you port it.")))))
  8.2107-
  8.2108-  (defmacro os-cond (&rest clauses)
  8.2109-    #+abcl `(cond ,@clauses)
  8.2110-    #-abcl (loop :for (test . body) :in clauses :when (eval test) :return `(progn ,@body)))
  8.2111-
  8.2112-  (detect-os))
  8.2113-
  8.2114-;;;; Environment variables: getting them, and parsing them.
  8.2115-(with-upgradability ()
  8.2116-  (defun getenv (x)
  8.2117-    "Query the environment, as in C getenv.
  8.2118-Beware: may return empty string if a variable is present but empty;
  8.2119-use getenvp to return NIL in such a case."
  8.2120-    (declare (ignorable x))
  8.2121-    #+(or abcl clasp clisp ecl xcl) (ext:getenv x)
  8.2122-    #+allegro (sys:getenv x)
  8.2123-    #+clozure (ccl:getenv x)
  8.2124-    #+cmucl (unix:unix-getenv x)
  8.2125-    #+scl (cdr (assoc x ext:*environment-list* :test #'string=))
  8.2126-    #+cormanlisp
  8.2127-    (let* ((buffer (ct:malloc 1))
  8.2128-           (cname (ct:lisp-string-to-c-string x))
  8.2129-           (needed-size (win:getenvironmentvariable cname buffer 0))
  8.2130-           (buffer1 (ct:malloc (1+ needed-size))))
  8.2131-      (prog1 (if (zerop (win:getenvironmentvariable cname buffer1 needed-size))
  8.2132-                 nil
  8.2133-                 (ct:c-string-to-lisp-string buffer1))
  8.2134-        (ct:free buffer)
  8.2135-        (ct:free buffer1)))
  8.2136-    #+gcl (system:getenv x)
  8.2137-    #+(or genera mezzano) nil
  8.2138-    #+lispworks (lispworks:environment-variable x)
  8.2139-    #+mcl (ccl:with-cstrs ((name x))
  8.2140-            (let ((value (_getenv name)))
  8.2141-              (unless (ccl:%null-ptr-p value)
  8.2142-                (ccl:%get-cstring value))))
  8.2143-    #+mkcl (#.(or (find-symbol* 'getenv :si nil) (find-symbol* 'getenv :mk-ext nil)) x)
  8.2144-    #+sbcl (sb-ext:posix-getenv x)
  8.2145-    #-(or abcl allegro clasp clisp clozure cmucl cormanlisp ecl gcl genera lispworks mcl mezzano mkcl sbcl scl xcl)
  8.2146-    (not-implemented-error 'getenv))
  8.2147-
  8.2148-  (defsetf getenv (x) (val)
  8.2149-    "Set an environment variable."
  8.2150-    (declare (ignorable x val))
  8.2151-    #+allegro `(setf (sys:getenv ,x) ,val)
  8.2152-    #+clasp `(ext:setenv ,x ,val)
  8.2153-    #+clisp `(system::setenv ,x ,val)
  8.2154-    #+clozure `(ccl:setenv ,x ,val)
  8.2155-    #+cmucl `(unix:unix-setenv ,x ,val 1)
  8.2156-    #+(or ecl clasp) `(ext:setenv ,x ,val)
  8.2157-    #+lispworks `(setf (lispworks:environment-variable ,x) ,val)
  8.2158-    #+mkcl `(mkcl:setenv ,x ,val)
  8.2159-    #+sbcl `(progn (require :sb-posix) (symbol-call :sb-posix :setenv ,x ,val 1))
  8.2160-    #-(or allegro clasp clisp clozure cmucl ecl lispworks mkcl sbcl)
  8.2161-    '(not-implemented-error '(setf getenv)))
  8.2162-
  8.2163-  (defun getenvp (x)
  8.2164-    "Predicate that is true if the named variable is present in the libc environment,
  8.2165-then returning the non-empty string value of the variable"
  8.2166-    (let ((g (getenv x))) (and (not (emptyp g)) g))))
  8.2167-
  8.2168-
  8.2169-;;;; implementation-identifier
  8.2170-;;
  8.2171-;; produce a string to identify current implementation.
  8.2172-;; Initially stolen from SLIME's SWANK, completely rewritten since.
  8.2173-;; We're back to runtime checking, for the sake of e.g. ABCL.
  8.2174-
  8.2175-(with-upgradability ()
  8.2176-  (defun first-feature (feature-sets)
  8.2177-    "A helper for various feature detection functions"
  8.2178-    (dolist (x feature-sets)
  8.2179-      (multiple-value-bind (short long feature-expr)
  8.2180-          (if (consp x)
  8.2181-              (values (first x) (second x) (cons :or (rest x)))
  8.2182-              (values x x x))
  8.2183-        (when (featurep feature-expr)
  8.2184-          (return (values short long))))))
  8.2185-
  8.2186-  (defun implementation-type ()
  8.2187-    "The type of Lisp implementation used, as a short UIOP-standardized keyword"
  8.2188-    (first-feature
  8.2189-     '(:abcl (:acl :allegro) (:ccl :clozure) :clisp (:corman :cormanlisp)
  8.2190-       (:cmu :cmucl :cmu) :clasp :ecl :gcl
  8.2191-       (:lwpe :lispworks-personal-edition) (:lw :lispworks)
  8.2192-       :mcl :mezzano :mkcl :sbcl :scl (:smbx :symbolics) :xcl)))
  8.2193-
  8.2194-  (defvar *implementation-type* (implementation-type)
  8.2195-    "The type of Lisp implementation used, as a short UIOP-standardized keyword")
  8.2196-
  8.2197-  (defun operating-system ()
  8.2198-    "The operating system of the current host"
  8.2199-    (first-feature
  8.2200-     '(:cygwin
  8.2201-       (:win :windows :mswindows :win32 :mingw32) ;; try cygwin first!
  8.2202-       (:linux :linux :linux-target) ;; for GCL at least, must appear before :bsd
  8.2203-       (:macosx :macosx :darwin :darwin-target :apple) ; also before :bsd
  8.2204-       (:solaris :solaris :sunos)
  8.2205-       (:bsd :bsd :freebsd :netbsd :openbsd :dragonfly)
  8.2206-       :unix
  8.2207-       :genera
  8.2208-       :mezzano)))
  8.2209-
  8.2210-  (defun architecture ()
  8.2211-    "The CPU architecture of the current host"
  8.2212-    (first-feature
  8.2213-     '((:x64 :x86-64 :x86_64 :x8664-target :amd64 (:and :word-size=64 :pc386))
  8.2214-       (:x86 :x86 :i386 :i486 :i586 :i686 :pentium3 :pentium4 :pc386 :iapx386 :x8632-target)
  8.2215-       (:ppc64 :ppc64 :ppc64-target) (:ppc32 :ppc32 :ppc32-target :ppc :powerpc)
  8.2216-       :hppa64 :hppa :sparc64 (:sparc32 :sparc32 :sparc)
  8.2217-       :mipsel :mipseb :mips :alpha
  8.2218-       (:arm64 :arm64 :aarch64 :armv8l :armv8b :aarch64_be :|aarch64|)
  8.2219-       (:arm :arm :arm-target) :vlm :imach
  8.2220-       ;; Java comes last: if someone uses C via CFFI or otherwise JNA or JNI,
  8.2221-       ;; we may have to segregate the code still by architecture.
  8.2222-       (:java :java :java-1.4 :java-1.5 :java-1.6 :java-1.7))))
  8.2223-
  8.2224-  #+clozure
  8.2225-  (defun ccl-fasl-version ()
  8.2226-    ;; the fasl version is target-dependent from CCL 1.8 on.
  8.2227-    (or (let ((s 'ccl::target-fasl-version))
  8.2228-          (and (fboundp s) (funcall s)))
  8.2229-        (and (boundp 'ccl::fasl-version)
  8.2230-             (symbol-value 'ccl::fasl-version))
  8.2231-        (error "Can't determine fasl version.")))
  8.2232-
  8.2233-  (defun lisp-version-string ()
  8.2234-    "return a string that identifies the current Lisp implementation version"
  8.2235-    (let ((s (lisp-implementation-version)))
  8.2236-      (car ; as opposed to OR, this idiom prevents some unreachable code warning
  8.2237-       (list
  8.2238-        #+allegro
  8.2239-        (format nil "~A~@[~A~]~@[~A~]~@[~A~]"
  8.2240-                excl::*common-lisp-version-number*
  8.2241-                ;; M means "modern", as opposed to ANSI-compatible mode (which I consider default)
  8.2242-                (and (eq excl:*current-case-mode* :case-sensitive-lower) "M")
  8.2243-                ;; Note if not using International ACL
  8.2244-                ;; see http://www.franz.com/support/documentation/8.1/doc/operators/excl/ics-target-case.htm
  8.2245-                (excl:ics-target-case (:-ics "8"))
  8.2246-                (and (member :smp *features*) "S"))
  8.2247-        #+armedbear (format nil "~a-fasl~a" s system::*fasl-version*)
  8.2248-        #+clisp
  8.2249-        (subseq s 0 (position #\space s)) ; strip build information (date, etc.)
  8.2250-        #+clozure
  8.2251-        (format nil "~d.~d-f~d" ; shorten for windows
  8.2252-                ccl::*openmcl-major-version*
  8.2253-                ccl::*openmcl-minor-version*
  8.2254-                (logand (ccl-fasl-version) #xFF))
  8.2255-        #+cmucl (substitute #\- #\/ s)
  8.2256-        #+scl (format nil "~A~A" s
  8.2257-                      ;; ANSI upper case vs lower case.
  8.2258-                      (ecase ext:*case-mode* (:upper "") (:lower "l")))
  8.2259-        #+ecl (format nil "~A~@[-~A~]" s
  8.2260-                      (let ((vcs-id (ext:lisp-implementation-vcs-id)))
  8.2261-                        (unless (equal vcs-id "UNKNOWN")
  8.2262-                          (subseq vcs-id 0 (min (length vcs-id) 8)))))
  8.2263-        #+gcl (subseq s (1+ (position #\space s)))
  8.2264-        #+genera
  8.2265-        (multiple-value-bind (major minor) (sct:get-system-version "System")
  8.2266-          (format nil "~D.~D" major minor))
  8.2267-        #+mcl (subseq s 8) ; strip the leading "Version "
  8.2268-        #+mezzano (format nil "~A-~D"
  8.2269-                          (subseq s 0 (position #\space s)) ; strip commit hash
  8.2270-                          sys.int::*llf-version*)
  8.2271-        ;; seems like there should be a shorter way to do this, like ACALL.
  8.2272-        #+mkcl (or
  8.2273-                (let ((fname (find-symbol* '#:git-describe-this-mkcl :mkcl nil)))
  8.2274-                  (when (and fname (fboundp fname))
  8.2275-                    (funcall fname)))
  8.2276-                s)
  8.2277-        s))))
  8.2278-
  8.2279-  (defun implementation-identifier ()
  8.2280-    "Return a string that identifies the ABI of the current implementation,
  8.2281-suitable for use as a directory name to segregate Lisp FASLs, C dynamic libraries, etc."
  8.2282-    (substitute-if
  8.2283-     #\_ #'(lambda (x) (find x " /:;&^\\|?<>(){}[]$#`'\""))
  8.2284-     (format nil "~(~a~@{~@[-~a~]~}~)"
  8.2285-             (or (implementation-type) (lisp-implementation-type))
  8.2286-             (lisp-version-string)
  8.2287-             (or (operating-system) (software-type))
  8.2288-             (or (architecture) (machine-type))))))
  8.2289-
  8.2290-
  8.2291-;;;; Other system information
  8.2292-
  8.2293-(with-upgradability ()
  8.2294-  (defun hostname ()
  8.2295-    "return the hostname of the current host"
  8.2296-    #+(or abcl clasp clozure cmucl ecl genera lispworks mcl mezzano mkcl sbcl scl xcl) (machine-instance)
  8.2297-    #+cormanlisp "localhost" ;; is there a better way? Does it matter?
  8.2298-    #+allegro (symbol-call :excl.osi :gethostname)
  8.2299-    #+clisp (first (split-string (machine-instance) :separator " "))
  8.2300-    #+gcl (system:gethostname)))
  8.2301-
  8.2302-
  8.2303-;;; Current directory
  8.2304-(with-upgradability ()
  8.2305-
  8.2306-  #+cmucl
  8.2307-  (defun parse-unix-namestring* (unix-namestring)
  8.2308-    "variant of LISP::PARSE-UNIX-NAMESTRING that returns a pathname object"
  8.2309-    (multiple-value-bind (host device directory name type version)
  8.2310-        (lisp::parse-unix-namestring unix-namestring 0 (length unix-namestring))
  8.2311-      (make-pathname :host (or host lisp::*unix-host*) :device device
  8.2312-                     :directory directory :name name :type type :version version)))
  8.2313-
  8.2314-  (defun getcwd ()
  8.2315-    "Get the current working directory as per POSIX getcwd(3), as a pathname object"
  8.2316-    (or #+(or abcl genera mezzano xcl) (truename *default-pathname-defaults*) ;; d-p-d is canonical!
  8.2317-        #+allegro (excl::current-directory)
  8.2318-        #+clisp (ext:default-directory)
  8.2319-        #+clozure (ccl:current-directory)
  8.2320-        #+(or cmucl scl) (#+cmucl parse-unix-namestring* #+scl lisp::parse-unix-namestring
  8.2321-                        (strcat (nth-value 1 (unix:unix-current-directory)) "/"))
  8.2322-        #+cormanlisp (pathname (pl::get-current-directory)) ;; Q: what type does it return?
  8.2323-        #+(or clasp ecl) (ext:getcwd)
  8.2324-        #+gcl (let ((*default-pathname-defaults* #p"")) (truename #p""))
  8.2325-        #+lispworks (hcl:get-working-directory)
  8.2326-        #+mkcl (mk-ext:getcwd)
  8.2327-        #+sbcl (sb-ext:parse-native-namestring (sb-unix:posix-getcwd/))
  8.2328-        #+xcl (extensions:current-directory)
  8.2329-        (not-implemented-error 'getcwd)))
  8.2330-
  8.2331-  (defun chdir (x)
  8.2332-    "Change current directory, as per POSIX chdir(2), to a given pathname object"
  8.2333-    (if-let (x (pathname x))
  8.2334-      #+(or abcl genera mezzano xcl) (setf *default-pathname-defaults* (truename x)) ;; d-p-d is canonical!
  8.2335-      #+allegro (excl:chdir x)
  8.2336-      #+clisp (ext:cd x)
  8.2337-      #+clozure (setf (ccl:current-directory) x)
  8.2338-      #+(or cmucl scl) (unix:unix-chdir (ext:unix-namestring x))
  8.2339-      #+cormanlisp (unless (zerop (win32::_chdir (namestring x)))
  8.2340-                     (error "Could not set current directory to ~A" x))
  8.2341-      #+ecl (ext:chdir x)
  8.2342-      #+clasp (ext:chdir x t)
  8.2343-      #+gcl (system:chdir x)
  8.2344-      #+lispworks (hcl:change-directory x)
  8.2345-      #+mkcl (mk-ext:chdir x)
  8.2346-      #+sbcl (progn (require :sb-posix) (symbol-call :sb-posix :chdir (sb-ext:native-namestring x)))
  8.2347-      #-(or abcl allegro clasp clisp clozure cmucl cormanlisp ecl gcl genera lispworks mkcl sbcl scl xcl)
  8.2348-      (not-implemented-error 'chdir))))
  8.2349-
  8.2350-
  8.2351-;;;; -----------------------------------------------------------------
  8.2352-;;;; Windows shortcut support.  Based on:
  8.2353-;;;;
  8.2354-;;;; Jesse Hager: The Windows Shortcut File Format.
  8.2355-;;;; http://www.wotsit.org/list.asp?fc=13
  8.2356-
  8.2357-#-(or clisp genera) ; CLISP doesn't need it, and READ-SEQUENCE annoys old Genera that doesn't need it
  8.2358-(with-upgradability ()
  8.2359-  (defparameter *link-initial-dword* 76)
  8.2360-  (defparameter *link-guid* #(1 20 2 0 0 0 0 0 192 0 0 0 0 0 0 70))
  8.2361-
  8.2362-  (defun read-null-terminated-string (s)
  8.2363-    "Read a null-terminated string from an octet stream S"
  8.2364-    ;; note: doesn't play well with UNICODE
  8.2365-    (with-output-to-string (out)
  8.2366-      (loop :for code = (read-byte s)
  8.2367-            :until (zerop code)
  8.2368-            :do (write-char (code-char code) out))))
  8.2369-
  8.2370-  (defun read-little-endian (s &optional (bytes 4))
  8.2371-    "Read a number in little-endian format from an byte (octet) stream S,
  8.2372-the number having BYTES octets (defaulting to 4)."
  8.2373-    (loop :for i :from 0 :below bytes
  8.2374-          :sum (ash (read-byte s) (* 8 i))))
  8.2375-
  8.2376-  (defun parse-file-location-info (s)
  8.2377-    "helper to parse-windows-shortcut"
  8.2378-    (let ((start (file-position s))
  8.2379-          (total-length (read-little-endian s))
  8.2380-          (end-of-header (read-little-endian s))
  8.2381-          (fli-flags (read-little-endian s))
  8.2382-          (local-volume-offset (read-little-endian s))
  8.2383-          (local-offset (read-little-endian s))
  8.2384-          (network-volume-offset (read-little-endian s))
  8.2385-          (remaining-offset (read-little-endian s)))
  8.2386-      (declare (ignore total-length end-of-header local-volume-offset))
  8.2387-      (unless (zerop fli-flags)
  8.2388-        (cond
  8.2389-          ((logbitp 0 fli-flags)
  8.2390-           (file-position s (+ start local-offset)))
  8.2391-          ((logbitp 1 fli-flags)
  8.2392-           (file-position s (+ start
  8.2393-                               network-volume-offset
  8.2394-                               #x14))))
  8.2395-        (strcat (read-null-terminated-string s)
  8.2396-                (progn
  8.2397-                  (file-position s (+ start remaining-offset))
  8.2398-                  (read-null-terminated-string s))))))
  8.2399-
  8.2400-  (defun parse-windows-shortcut (pathname)
  8.2401-    "From a .lnk windows shortcut, extract the pathname linked to"
  8.2402-    ;; NB: doesn't do much checking & doesn't look like it will work well with UNICODE.
  8.2403-    (with-open-file (s pathname :element-type '(unsigned-byte 8))
  8.2404-      (handler-case
  8.2405-          (when (and (= (read-little-endian s) *link-initial-dword*)
  8.2406-                     (let ((header (make-array (length *link-guid*))))
  8.2407-                       (read-sequence header s)
  8.2408-                       (equalp header *link-guid*)))
  8.2409-            (let ((flags (read-little-endian s)))
  8.2410-              (file-position s 76)        ;skip rest of header
  8.2411-              (when (logbitp 0 flags)
  8.2412-                ;; skip shell item id list
  8.2413-                (let ((length (read-little-endian s 2)))
  8.2414-                  (file-position s (+ length (file-position s)))))
  8.2415-              (cond
  8.2416-                ((logbitp 1 flags)
  8.2417-                 (parse-file-location-info s))
  8.2418-                (t
  8.2419-                 (when (logbitp 2 flags)
  8.2420-                   ;; skip description string
  8.2421-                   (let ((length (read-little-endian s 2)))
  8.2422-                     (file-position s (+ length (file-position s)))))
  8.2423-                 (when (logbitp 3 flags)
  8.2424-                   ;; finally, our pathname
  8.2425-                   (let* ((length (read-little-endian s 2))
  8.2426-                          (buffer (make-array length)))
  8.2427-                     (read-sequence buffer s)
  8.2428-                     (map 'string #'code-char buffer)))))))
  8.2429-        (end-of-file (c)
  8.2430-          (declare (ignore c))
  8.2431-          nil)))))
  8.2432-
  8.2433-
  8.2434-;;;; -------------------------------------------------------------------------
  8.2435-;;;; Portability layer around Common Lisp pathnames
  8.2436-;; This layer allows for portable manipulation of pathname objects themselves,
  8.2437-;; which all is necessary prior to any access the filesystem or environment.
  8.2438-
  8.2439-(uiop/package:define-package :uiop/pathname
  8.2440-  (:nicknames :asdf/pathname) ;; deprecated. Used by ceramic
  8.2441-  (:use :uiop/common-lisp :uiop/package :uiop/utility :uiop/os)
  8.2442-  (:export
  8.2443-   ;; Making and merging pathnames, portably
  8.2444-   #:normalize-pathname-directory-component #:denormalize-pathname-directory-component
  8.2445-   #:merge-pathname-directory-components #:*unspecific-pathname-type* #:make-pathname*
  8.2446-   #:make-pathname-component-logical #:make-pathname-logical
  8.2447-   #:merge-pathnames*
  8.2448-   #:nil-pathname #:*nil-pathname* #:with-pathname-defaults
  8.2449-   ;; Predicates
  8.2450-   #:pathname-equal #:logical-pathname-p #:physical-pathname-p #:physicalize-pathname
  8.2451-   #:absolute-pathname-p #:relative-pathname-p #:hidden-pathname-p #:file-pathname-p
  8.2452-   ;; Directories
  8.2453-   #:pathname-directory-pathname #:pathname-parent-directory-pathname
  8.2454-   #:directory-pathname-p #:ensure-directory-pathname
  8.2455-   ;; Parsing filenames
  8.2456-   #:split-name-type #:parse-unix-namestring #:unix-namestring
  8.2457-   #:split-unix-namestring-directory-components
  8.2458-   ;; Absolute and relative pathnames
  8.2459-   #:subpathname #:subpathname*
  8.2460-   #:ensure-absolute-pathname
  8.2461-   #:pathname-root #:pathname-host-pathname
  8.2462-   #:subpathp #:enough-pathname #:with-enough-pathname #:call-with-enough-pathname
  8.2463-   ;; Checking constraints
  8.2464-   #:ensure-pathname ;; implemented in filesystem.lisp to accommodate for existence constraints
  8.2465-   ;; Wildcard pathnames
  8.2466-   #:*wild* #:*wild-file* #:*wild-file-for-directory* #:*wild-directory*
  8.2467-   #:*wild-inferiors* #:*wild-path* #:wilden
  8.2468-   ;; Translate a pathname
  8.2469-   #:relativize-directory-component #:relativize-pathname-directory
  8.2470-   #:directory-separator-for-host #:directorize-pathname-host-device
  8.2471-   #:translate-pathname*
  8.2472-   #:*output-translation-function*))
  8.2473-(in-package :uiop/pathname)
  8.2474-
  8.2475-;;; Normalizing pathnames across implementations
  8.2476-
  8.2477-(with-upgradability ()
  8.2478-  (defun normalize-pathname-directory-component (directory)
  8.2479-    "Convert the DIRECTORY component from a format usable by the underlying
  8.2480-implementation's MAKE-PATHNAME and other primitives to a CLHS-standard format
  8.2481-that is a list and not a string."
  8.2482-    (cond
  8.2483-      #-(or cmucl sbcl scl) ;; these implementations already normalize directory components.
  8.2484-      ((stringp directory) `(:absolute ,directory))
  8.2485-      ((or (null directory)
  8.2486-           (and (consp directory) (member (first directory) '(:absolute :relative))))
  8.2487-       directory)
  8.2488-      #+gcl
  8.2489-      ((consp directory)
  8.2490-       (cons :relative directory))
  8.2491-      (t
  8.2492-       (parameter-error (compatfmt "~@<~S: Unrecognized pathname directory component ~S~@:>")
  8.2493-                        'normalize-pathname-directory-component directory))))
  8.2494-
  8.2495-  (defun denormalize-pathname-directory-component (directory-component)
  8.2496-    "Convert the DIRECTORY-COMPONENT from a CLHS-standard format to a format usable
  8.2497-by the underlying implementation's MAKE-PATHNAME and other primitives"
  8.2498-    directory-component)
  8.2499-
  8.2500-  (defun merge-pathname-directory-components (specified defaults)
  8.2501-    "Helper for MERGE-PATHNAMES* that handles directory components"
  8.2502-    (let ((directory (normalize-pathname-directory-component specified)))
  8.2503-      (ecase (first directory)
  8.2504-        ((nil) defaults)
  8.2505-        (:absolute specified)
  8.2506-        (:relative
  8.2507-         (let ((defdir (normalize-pathname-directory-component defaults))
  8.2508-               (reldir (cdr directory)))
  8.2509-           (cond
  8.2510-             ((null defdir)
  8.2511-              directory)
  8.2512-             ((not (eq :back (first reldir)))
  8.2513-              (append defdir reldir))
  8.2514-             (t
  8.2515-              (loop :with defabs = (first defdir)
  8.2516-                    :with defrev = (reverse (rest defdir))
  8.2517-                    :while (and (eq :back (car reldir))
  8.2518-                                (or (and (eq :absolute defabs) (null defrev))
  8.2519-                                    (stringp (car defrev))))
  8.2520-                    :do (pop reldir) (pop defrev)
  8.2521-                    :finally (return (cons defabs (append (reverse defrev) reldir)))))))))))
  8.2522-
  8.2523-  ;; Giving :unspecific as :type argument to make-pathname is not portable.
  8.2524-  ;; See CLHS make-pathname and 19.2.2.2.3.
  8.2525-  ;; This will be :unspecific if supported, or NIL if not.
  8.2526-  (defparameter *unspecific-pathname-type*
  8.2527-    #+(or abcl allegro clozure cmucl lispworks sbcl scl) :unspecific
  8.2528-    #+(or genera clasp clisp ecl mkcl gcl xcl #|These haven't been tested:|# cormanlisp mcl mezzano) nil
  8.2529-    "Unspecific type component to use with the underlying implementation's MAKE-PATHNAME")
  8.2530-
  8.2531-  (defun make-pathname* (&rest keys &key directory host device name type version defaults
  8.2532-                                      #+scl &allow-other-keys)
  8.2533-    "Takes arguments like CL:MAKE-PATHNAME in the CLHS, and
  8.2534-   tries hard to make a pathname that will actually behave as documented,
  8.2535-   despite the peculiarities of each implementation. DEPRECATED: just use MAKE-PATHNAME."
  8.2536-    (declare (ignore host device directory name type version defaults))
  8.2537-    (apply 'make-pathname keys))
  8.2538-
  8.2539-  (defun make-pathname-component-logical (x)
  8.2540-    "Make a pathname component suitable for use in a logical-pathname"
  8.2541-    (typecase x
  8.2542-      ((eql :unspecific) nil)
  8.2543-      #+clisp (string (string-upcase x))
  8.2544-      #+clisp (cons (mapcar 'make-pathname-component-logical x))
  8.2545-      (t x)))
  8.2546-
  8.2547-  (defun make-pathname-logical (pathname host)
  8.2548-    "Take a PATHNAME's directory, name, type and version components,
  8.2549-and make a new pathname with corresponding components and specified logical HOST"
  8.2550-    (make-pathname
  8.2551-     :host host
  8.2552-     :directory (make-pathname-component-logical (pathname-directory pathname))
  8.2553-     :name (make-pathname-component-logical (pathname-name pathname))
  8.2554-     :type (make-pathname-component-logical (pathname-type pathname))
  8.2555-     :version (make-pathname-component-logical (pathname-version pathname))))
  8.2556-
  8.2557-  (defun merge-pathnames* (specified &optional (defaults *default-pathname-defaults*))
  8.2558-    "MERGE-PATHNAMES* is like MERGE-PATHNAMES except that
  8.2559-if the SPECIFIED pathname does not have an absolute directory,
  8.2560-then the HOST and DEVICE both come from the DEFAULTS, whereas
  8.2561-if the SPECIFIED pathname does have an absolute directory,
  8.2562-then the HOST and DEVICE both come from the SPECIFIED pathname.
  8.2563-This is what users want on a modern Unix or Windows operating system,
  8.2564-unlike the MERGE-PATHNAMES behavior.
  8.2565-Also, if either argument is NIL, then the other argument is returned unmodified;
  8.2566-this is unlike MERGE-PATHNAMES which always merges with a pathname,
  8.2567-by default *DEFAULT-PATHNAME-DEFAULTS*, which cannot be NIL."
  8.2568-    (when (null specified) (return-from merge-pathnames* defaults))
  8.2569-    (when (null defaults) (return-from merge-pathnames* specified))
  8.2570-    #+scl
  8.2571-    (ext:resolve-pathname specified defaults)
  8.2572-    #-scl
  8.2573-    (let* ((specified (pathname specified))
  8.2574-           (defaults (pathname defaults))
  8.2575-           (directory (normalize-pathname-directory-component (pathname-directory specified)))
  8.2576-           (name (or (pathname-name specified) (pathname-name defaults)))
  8.2577-           (type (or (pathname-type specified) (pathname-type defaults)))
  8.2578-           (version (or (pathname-version specified) (pathname-version defaults))))
  8.2579-      (labels ((unspecific-handler (p)
  8.2580-                 (if (typep p 'logical-pathname) #'make-pathname-component-logical #'identity)))
  8.2581-        (multiple-value-bind (host device directory unspecific-handler)
  8.2582-            (ecase (first directory)
  8.2583-              ((:absolute)
  8.2584-               (values (pathname-host specified)
  8.2585-                       (pathname-device specified)
  8.2586-                       directory
  8.2587-                       (unspecific-handler specified)))
  8.2588-              ((nil :relative)
  8.2589-               (values (pathname-host defaults)
  8.2590-                       (pathname-device defaults)
  8.2591-                       (merge-pathname-directory-components directory (pathname-directory defaults))
  8.2592-                       (unspecific-handler defaults))))
  8.2593-          (make-pathname :host host :device device :directory directory
  8.2594-                         :name (funcall unspecific-handler name)
  8.2595-                         :type (funcall unspecific-handler type)
  8.2596-                         :version (funcall unspecific-handler version))))))
  8.2597-
  8.2598-  (defun logical-pathname-p (x)
  8.2599-    "is X a logical-pathname?"
  8.2600-    (typep x 'logical-pathname))
  8.2601-
  8.2602-  (defun physical-pathname-p (x)
  8.2603-    "is X a pathname that is not a logical-pathname?"
  8.2604-    (and (pathnamep x) (not (logical-pathname-p x))))
  8.2605-
  8.2606-  (defun physicalize-pathname (x)
  8.2607-    "if X is a logical pathname, use translate-logical-pathname on it."
  8.2608-    ;; Ought to be the same as translate-logical-pathname, except the latter borks on CLISP
  8.2609-    (let ((p (when x (pathname x))))
  8.2610-      (if (logical-pathname-p p) (translate-logical-pathname p) p)))
  8.2611-
  8.2612-  (defun nil-pathname (&optional (defaults *default-pathname-defaults*))
  8.2613-    "A pathname that is as neutral as possible for use as defaults
  8.2614-when merging, making or parsing pathnames"
  8.2615-    ;; 19.2.2.2.1 says a NIL host can mean a default host;
  8.2616-    ;; see also "valid physical pathname host" in the CLHS glossary, that suggests
  8.2617-    ;; strings and lists of strings or :unspecific
  8.2618-    ;; But CMUCL decides to die on NIL.
  8.2619-    ;; MCL has issues with make-pathname, nil and defaulting
  8.2620-    (declare (ignorable defaults))
  8.2621-    #.`(make-pathname :directory nil :name nil :type nil :version nil
  8.2622-                      :device (or #+(and mkcl os-unix) :unspecific)
  8.2623-                      :host (or #+cmucl lisp::*unix-host* #+(and mkcl os-unix) "localhost")
  8.2624-                      #+scl ,@'(:scheme nil :scheme-specific-part nil
  8.2625-                                :username nil :password nil :parameters nil :query nil :fragment nil)
  8.2626-                      ;; the default shouldn't matter, but we really want something physical
  8.2627-                      #-mcl ,@'(:defaults defaults)))
  8.2628-
  8.2629-  (defvar *nil-pathname* (nil-pathname (physicalize-pathname (user-homedir-pathname)))
  8.2630-    "A pathname that is as neutral as possible for use as defaults
  8.2631-when merging, making or parsing pathnames")
  8.2632-
  8.2633-  (defmacro with-pathname-defaults ((&optional defaults) &body body)
  8.2634-    "Execute BODY in a context where the *DEFAULT-PATHNAME-DEFAULTS* is as specified,
  8.2635-where leaving the defaults NIL or unspecified means a (NIL-PATHNAME), except
  8.2636-on ABCL, Genera and XCL, where it remains unchanged for it doubles as current-directory."
  8.2637-    `(let ((*default-pathname-defaults*
  8.2638-             ,(or defaults
  8.2639-                  #-(or abcl genera xcl) '*nil-pathname*
  8.2640-                  #+(or abcl genera xcl) '*default-pathname-defaults*)))
  8.2641-       ,@body)))
  8.2642-
  8.2643-
  8.2644-;;; Some pathname predicates
  8.2645-(with-upgradability ()
  8.2646-  (defun pathname-equal (p1 p2)
  8.2647-    "Are the two pathnames P1 and P2 reasonably equal in the paths they denote?"
  8.2648-    (when (stringp p1) (setf p1 (pathname p1)))
  8.2649-    (when (stringp p2) (setf p2 (pathname p2)))
  8.2650-    (flet ((normalize-component (x)
  8.2651-             (unless (member x '(nil :unspecific :newest (:relative)) :test 'equal)
  8.2652-               x)))
  8.2653-      (macrolet ((=? (&rest accessors)
  8.2654-                   (flet ((frob (x)
  8.2655-                            (reduce 'list (cons 'normalize-component accessors)
  8.2656-                                    :initial-value x :from-end t)))
  8.2657-                     `(equal ,(frob 'p1) ,(frob 'p2)))))
  8.2658-        (or (and (null p1) (null p2))
  8.2659-            (and (pathnamep p1) (pathnamep p2)
  8.2660-                 (and (=? pathname-host)
  8.2661-                      #-(and mkcl os-unix) (=? pathname-device)
  8.2662-                      (=? normalize-pathname-directory-component pathname-directory)
  8.2663-                      (=? pathname-name)
  8.2664-                      (=? pathname-type)
  8.2665-                      #-mkcl (=? pathname-version)))))))
  8.2666-
  8.2667-  (defun absolute-pathname-p (pathspec)
  8.2668-    "If PATHSPEC is a pathname or namestring object that parses as a pathname
  8.2669-possessing an :ABSOLUTE directory component, return the (parsed) pathname.
  8.2670-Otherwise return NIL"
  8.2671-    (and pathspec
  8.2672-         (typep pathspec '(or null pathname string))
  8.2673-         (let ((pathname (pathname pathspec)))
  8.2674-           (and (eq :absolute (car (normalize-pathname-directory-component
  8.2675-                                    (pathname-directory pathname))))
  8.2676-                pathname))))
  8.2677-
  8.2678-  (defun relative-pathname-p (pathspec)
  8.2679-    "If PATHSPEC is a pathname or namestring object that parses as a pathname
  8.2680-possessing a :RELATIVE or NIL directory component, return the (parsed) pathname.
  8.2681-Otherwise return NIL"
  8.2682-    (and pathspec
  8.2683-         (typep pathspec '(or null pathname string))
  8.2684-         (let* ((pathname (pathname pathspec))
  8.2685-                (directory (normalize-pathname-directory-component
  8.2686-                            (pathname-directory pathname))))
  8.2687-           (when (or (null directory) (eq :relative (car directory)))
  8.2688-             pathname))))
  8.2689-
  8.2690-  (defun hidden-pathname-p (pathname)
  8.2691-    "Return a boolean that is true if the pathname is hidden as per Unix style,
  8.2692-i.e. its name starts with a dot."
  8.2693-    (and pathname (equal (first-char (pathname-name pathname)) #\.)))
  8.2694-
  8.2695-  (defun file-pathname-p (pathname)
  8.2696-    "Does PATHNAME represent a file, i.e. has a non-null NAME component?
  8.2697-
  8.2698-Accepts NIL, a string (converted through PARSE-NAMESTRING) or a PATHNAME.
  8.2699-
  8.2700-Note that this does _not_ check to see that PATHNAME points to an
  8.2701-actually-existing file.
  8.2702-
  8.2703-Returns the (parsed) PATHNAME when true"
  8.2704-    (when pathname
  8.2705-      (let ((pathname (pathname pathname)))
  8.2706-        (unless (and (member (pathname-name pathname) '(nil :unspecific "") :test 'equal)
  8.2707-                     (member (pathname-type pathname) '(nil :unspecific "") :test 'equal))
  8.2708-          pathname)))))
  8.2709-
  8.2710-
  8.2711-;;; Directory pathnames
  8.2712-(with-upgradability ()
  8.2713-  (defun pathname-directory-pathname (pathname)
  8.2714-    "Returns a new pathname with same HOST, DEVICE, DIRECTORY as PATHNAME,
  8.2715-and NIL NAME, TYPE and VERSION components"
  8.2716-    (when pathname
  8.2717-      (make-pathname :name nil :type nil :version nil :defaults pathname)))
  8.2718-
  8.2719-  (defun pathname-parent-directory-pathname (pathname)
  8.2720-    "Returns a new pathname that corresponds to the parent of the current pathname's directory,
  8.2721-i.e. removing one level of depth in the DIRECTORY component. e.g. if pathname is
  8.2722-Unix pathname /foo/bar/baz/file.type then return /foo/bar/"
  8.2723-    (when pathname
  8.2724-      (make-pathname :name nil :type nil :version nil
  8.2725-                     :directory (merge-pathname-directory-components
  8.2726-                                 '(:relative :back) (pathname-directory pathname))
  8.2727-                     :defaults pathname)))
  8.2728-
  8.2729-  (defun directory-pathname-p (pathname)
  8.2730-    "Does PATHNAME represent a directory?
  8.2731-
  8.2732-A directory-pathname is a pathname _without_ a filename. The three
  8.2733-ways that the filename components can be missing are for it to be NIL,
  8.2734-:UNSPECIFIC or the empty string.
  8.2735-
  8.2736-Note that this does _not_ check to see that PATHNAME points to an
  8.2737-actually-existing directory."
  8.2738-    (when pathname
  8.2739-      ;; I tried using Allegro's excl:file-directory-p, but this cannot be done,
  8.2740-      ;; because it rejects apparently legal pathnames as
  8.2741-      ;; ill-formed. [2014/02/10:rpg]
  8.2742-      (let ((pathname (pathname pathname)))
  8.2743-        (flet ((check-one (x)
  8.2744-                 (member x '(nil :unspecific) :test 'equal)))
  8.2745-          (and (not (wild-pathname-p pathname))
  8.2746-               (check-one (pathname-name pathname))
  8.2747-               (check-one (pathname-type pathname))
  8.2748-               t)))))
  8.2749-
  8.2750-  (defun ensure-directory-pathname (pathspec &optional (on-error 'error))
  8.2751-    "Converts the non-wild pathname designator PATHSPEC to directory form."
  8.2752-    (cond
  8.2753-      ((stringp pathspec)
  8.2754-       (ensure-directory-pathname (pathname pathspec)))
  8.2755-      ((not (pathnamep pathspec))
  8.2756-       (call-function on-error (compatfmt "~@<Invalid pathname designator ~S~@:>") pathspec))
  8.2757-      ((wild-pathname-p pathspec)
  8.2758-       (call-function on-error (compatfmt "~@<Can't reliably convert wild pathname ~3i~_~S~@:>") pathspec))
  8.2759-      ((directory-pathname-p pathspec)
  8.2760-       pathspec)
  8.2761-      (t
  8.2762-       (handler-case
  8.2763-           (make-pathname :directory (append (or (normalize-pathname-directory-component
  8.2764-                                                  (pathname-directory pathspec))
  8.2765-                                                 (list :relative))
  8.2766-                                             (list #-genera (file-namestring pathspec)
  8.2767-                                                   ;; On Genera's native filesystem (LMFS),
  8.2768-                                                   ;; directories have a type and version
  8.2769-                                                   ;; which must be ignored when converting
  8.2770-                                                   ;; to a directory pathname
  8.2771-                                                   #+genera (if (typep pathspec 'fs:lmfs-pathname)
  8.2772-                                                                (pathname-name pathspec)
  8.2773-                                                                (file-namestring pathspec))))
  8.2774-                          :name nil :type nil :version nil :defaults pathspec)
  8.2775-         (error (c) (call-function on-error (compatfmt "~@<error while trying to create a directory pathname for ~S: ~A~@:>") pathspec c)))))))
  8.2776-
  8.2777-
  8.2778-;;; Parsing filenames
  8.2779-(with-upgradability ()
  8.2780-  (declaim (ftype function ensure-pathname)) ; forward reference
  8.2781-
  8.2782-  (defun split-unix-namestring-directory-components
  8.2783-      (unix-namestring &key ensure-directory dot-dot)
  8.2784-    "Splits the path string UNIX-NAMESTRING, returning four values:
  8.2785-A flag that is either :absolute or :relative, indicating
  8.2786-   how the rest of the values are to be interpreted.
  8.2787-A directory path --- a list of strings and keywords, suitable for
  8.2788-   use with MAKE-PATHNAME when prepended with the flag value.
  8.2789-   Directory components with an empty name or the name . are removed.
  8.2790-   Any directory named .. is read as DOT-DOT, or :BACK if it's NIL (not :UP).
  8.2791-A last-component, either a file-namestring including type extension,
  8.2792-   or NIL in the case of a directory pathname.
  8.2793-A flag that is true iff the unix-style-pathname was just
  8.2794-   a file-namestring without / path specification.
  8.2795-ENSURE-DIRECTORY forces the namestring to be interpreted as a directory pathname:
  8.2796-the third return value will be NIL, and final component of the namestring
  8.2797-will be treated as part of the directory path.
  8.2798-
  8.2799-An empty string is thus read as meaning a pathname object with all fields nil.
  8.2800-
  8.2801-Note that colon characters #\: will NOT be interpreted as host specification.
  8.2802-Absolute pathnames are only appropriate on Unix-style systems.
  8.2803-
  8.2804-The intention of this function is to support structured component names,
  8.2805-e.g., \(:file \"foo/bar\"\), which will be unpacked to relative pathnames."
  8.2806-    (check-type unix-namestring string)
  8.2807-    (check-type dot-dot (member nil :back :up))
  8.2808-    (if (and (not (find #\/ unix-namestring)) (not ensure-directory)
  8.2809-             (plusp (length unix-namestring)))
  8.2810-        (values :relative () unix-namestring t)
  8.2811-        (let* ((components (split-string unix-namestring :separator "/"))
  8.2812-               (last-comp (car (last components))))
  8.2813-          (multiple-value-bind (relative components)
  8.2814-              (if (equal (first components) "")
  8.2815-                  (if (equal (first-char unix-namestring) #\/)
  8.2816-                      (values :absolute (cdr components))
  8.2817-                      (values :relative nil))
  8.2818-                  (values :relative components))
  8.2819-            (setf components (remove-if #'(lambda (x) (member x '("" ".") :test #'equal))
  8.2820-                                        components))
  8.2821-            (setf components (substitute (or dot-dot :back) ".." components :test #'equal))
  8.2822-            (cond
  8.2823-              ((equal last-comp "")
  8.2824-               (values relative components nil nil)) ; "" already removed from components
  8.2825-              (ensure-directory
  8.2826-               (values relative components nil nil))
  8.2827-              (t
  8.2828-               (values relative (butlast components) last-comp nil)))))))
  8.2829-
  8.2830-  (defun split-name-type (filename)
  8.2831-    "Split a filename into two values NAME and TYPE that are returned.
  8.2832-We assume filename has no directory component.
  8.2833-The last . if any separates name and type from from type,
  8.2834-except that if there is only one . and it is in first position,
  8.2835-the whole filename is the NAME with an empty type.
  8.2836-NAME is always a string.
  8.2837-For an empty type, *UNSPECIFIC-PATHNAME-TYPE* is returned."
  8.2838-    (check-type filename string)
  8.2839-    (assert (plusp (length filename)))
  8.2840-    (destructuring-bind (name &optional (type *unspecific-pathname-type*))
  8.2841-        (split-string filename :max 2 :separator ".")
  8.2842-      (if (equal name "")
  8.2843-          (values filename *unspecific-pathname-type*)
  8.2844-          (values name type))))
  8.2845-
  8.2846-  (defun parse-unix-namestring (name &rest keys &key type defaults dot-dot ensure-directory
  8.2847-                                &allow-other-keys)
  8.2848-    "Coerce NAME into a PATHNAME using standard Unix syntax.
  8.2849-
  8.2850-Unix syntax is used whether or not the underlying system is Unix;
  8.2851-on such non-Unix systems it is reliably usable only for relative pathnames.
  8.2852-This function is especially useful to manipulate relative pathnames portably,
  8.2853-where it is crucial to possess a portable pathname syntax independent of the underlying OS.
  8.2854-This is what PARSE-UNIX-NAMESTRING provides, and why we use it in ASDF.
  8.2855-
  8.2856-When given a PATHNAME object, just return it untouched.
  8.2857-When given NIL, just return NIL.
  8.2858-When given a non-null SYMBOL, first downcase its name and treat it as a string.
  8.2859-When given a STRING, portably decompose it into a pathname as below.
  8.2860-
  8.2861-#\\/ separates directory components.
  8.2862-
  8.2863-The last #\\/-separated substring is interpreted as follows:
  8.2864-1- If TYPE is :DIRECTORY or ENSURE-DIRECTORY is true,
  8.2865- the string is made the last directory component, and NAME and TYPE are NIL.
  8.2866- if the string is empty, it's the empty pathname with all slots NIL.
  8.2867-2- If TYPE is NIL, the substring is a file-namestring, and its NAME and TYPE
  8.2868- are separated by SPLIT-NAME-TYPE.
  8.2869-3- If TYPE is a string, it is the given TYPE, and the whole string is the NAME.
  8.2870-
  8.2871-Directory components with an empty name or the name \".\" are removed.
  8.2872-Any directory named \"..\" is read as DOT-DOT,
  8.2873-which must be one of :BACK or :UP and defaults to :BACK.
  8.2874-
  8.2875-HOST, DEVICE and VERSION components are taken from DEFAULTS,
  8.2876-which itself defaults to *NIL-PATHNAME*, also used if DEFAULTS is NIL.
  8.2877-No host or device can be specified in the string itself,
  8.2878-which makes it unsuitable for absolute pathnames outside Unix.
  8.2879-
  8.2880-For relative pathnames, these components (and hence the defaults) won't matter
  8.2881-if you use MERGE-PATHNAMES* but will matter if you use MERGE-PATHNAMES,
  8.2882-which is an important reason to always use MERGE-PATHNAMES*.
  8.2883-
  8.2884-Arbitrary keys are accepted, and the parse result is passed to ENSURE-PATHNAME
  8.2885-with those keys, removing TYPE DEFAULTS and DOT-DOT.
  8.2886-When you're manipulating pathnames that are supposed to make sense portably
  8.2887-even though the OS may not be Unixish, we recommend you use :WANT-RELATIVE T
  8.2888-to throw an error if the pathname is absolute"
  8.2889-    (block nil
  8.2890-      (check-type type (or null string (eql :directory)))
  8.2891-      (when ensure-directory
  8.2892-        (setf type :directory))
  8.2893-      (etypecase name
  8.2894-        ((or null pathname) (return name))
  8.2895-        (symbol
  8.2896-         (setf name (string-downcase name)))
  8.2897-        (string))
  8.2898-      (multiple-value-bind (relative path filename file-only)
  8.2899-          (split-unix-namestring-directory-components
  8.2900-           name :dot-dot dot-dot :ensure-directory (eq type :directory))
  8.2901-        (multiple-value-bind (name type)
  8.2902-            (cond
  8.2903-              ((or (eq type :directory) (null filename))
  8.2904-               (values nil nil))
  8.2905-              (type
  8.2906-               (values filename type))
  8.2907-              (t
  8.2908-               (split-name-type filename)))
  8.2909-            (let* ((directory
  8.2910-                    (unless file-only (cons relative path)))
  8.2911-                   (pathname
  8.2912-                    #-abcl
  8.2913-                    (make-pathname
  8.2914-                     :directory directory
  8.2915-                     :name name :type type
  8.2916-                     :defaults (or #-mcl defaults *nil-pathname*))
  8.2917-                    #+abcl
  8.2918-                    (if (and defaults
  8.2919-                             (ext:pathname-jar-p defaults)
  8.2920-                             (null directory))
  8.2921-                        ;; When DEFAULTS is a jar, it will have the directory we want
  8.2922-                        (make-pathname :name name :type type
  8.2923-                                       :defaults (or defaults *nil-pathname*))
  8.2924-                        (make-pathname :name name :type type
  8.2925-                                       :defaults (or defaults *nil-pathname*)
  8.2926-                                       :directory directory))))
  8.2927-              (apply 'ensure-pathname
  8.2928-                     pathname
  8.2929-                     (remove-plist-keys '(:type :dot-dot :defaults) keys)))))))
  8.2930-
  8.2931-  (defun unix-namestring (pathname)
  8.2932-    "Given a non-wild PATHNAME, return a Unix-style namestring for it.
  8.2933-If the PATHNAME is NIL or a STRING, return it unchanged.
  8.2934-
  8.2935-This only considers the DIRECTORY, NAME and TYPE components of the pathname.
  8.2936-This is a portable solution for representing relative pathnames,
  8.2937-But unless you are running on a Unix system, it is not a general solution
  8.2938-to representing native pathnames.
  8.2939-
  8.2940-An error is signaled if the argument is not NULL, a STRING or a PATHNAME,
  8.2941-or if it is a PATHNAME but some of its components are not recognized."
  8.2942-    (etypecase pathname
  8.2943-      ((or null string) pathname)
  8.2944-      (pathname
  8.2945-       (with-output-to-string (s)
  8.2946-         (flet ((err () (parameter-error "~S: invalid unix-namestring ~S"
  8.2947-                                         'unix-namestring pathname)))
  8.2948-           (let* ((dir (normalize-pathname-directory-component (pathname-directory pathname)))
  8.2949-                  (name (pathname-name pathname))
  8.2950-                  (name (and (not (eq name :unspecific)) name))
  8.2951-                  (type (pathname-type pathname))
  8.2952-                  (type (and (not (eq type :unspecific)) type)))
  8.2953-             (cond
  8.2954-               ((member dir '(nil :unspecific)))
  8.2955-               ((eq dir '(:relative)) (princ "./" s))
  8.2956-               ((consp dir)
  8.2957-                (destructuring-bind (relabs &rest dirs) dir
  8.2958-                  (or (member relabs '(:relative :absolute)) (err))
  8.2959-                  (when (eq relabs :absolute) (princ #\/ s))
  8.2960-                  (loop :for x :in dirs :do
  8.2961-                    (cond
  8.2962-                      ((member x '(:back :up)) (princ "../" s))
  8.2963-                      ((equal x "") (err))
  8.2964-                      ;;((member x '("." "..") :test 'equal) (err))
  8.2965-                      ((stringp x) (format s "~A/" x))
  8.2966-                      (t (err))))))
  8.2967-               (t (err)))
  8.2968-             (cond
  8.2969-               (name
  8.2970-                (unless (and (stringp name) (or (null type) (stringp type))) (err))
  8.2971-                (format s "~A~@[.~A~]" name type))
  8.2972-               (t
  8.2973-                (or (null type) (err)))))))))))
  8.2974-
  8.2975-;;; Absolute and relative pathnames
  8.2976-(with-upgradability ()
  8.2977-  (defun subpathname (pathname subpath &key type)
  8.2978-    "This function takes a PATHNAME and a SUBPATH and a TYPE.
  8.2979-If SUBPATH is already a PATHNAME object (not namestring),
  8.2980-and is an absolute pathname at that, it is returned unchanged;
  8.2981-otherwise, SUBPATH is turned into a relative pathname with given TYPE
  8.2982-as per PARSE-UNIX-NAMESTRING with :WANT-RELATIVE T :TYPE TYPE,
  8.2983-then it is merged with the PATHNAME-DIRECTORY-PATHNAME of PATHNAME."
  8.2984-    (or (and (pathnamep subpath) (absolute-pathname-p subpath))
  8.2985-        (merge-pathnames* (parse-unix-namestring subpath :type type :want-relative t)
  8.2986-                          (pathname-directory-pathname pathname))))
  8.2987-
  8.2988-  (defun subpathname* (pathname subpath &key type)
  8.2989-    "returns NIL if the base pathname is NIL, otherwise like SUBPATHNAME."
  8.2990-    (and pathname
  8.2991-         (subpathname (ensure-directory-pathname pathname) subpath :type type)))
  8.2992-
  8.2993-  (defun pathname-root (pathname)
  8.2994-    "return the root directory for the host and device of given PATHNAME"
  8.2995-    (make-pathname :directory '(:absolute)
  8.2996-                   :name nil :type nil :version nil
  8.2997-                   :defaults pathname ;; host device, and on scl, *some*
  8.2998-                   ;; scheme-specific parts: port username password, not others:
  8.2999-                   . #.(or #+scl '(:parameters nil :query nil :fragment nil))))
  8.3000-
  8.3001-  (defun pathname-host-pathname (pathname)
  8.3002-    "return a pathname with the same host as given PATHNAME, and all other fields NIL"
  8.3003-    (make-pathname :directory nil
  8.3004-                   :name nil :type nil :version nil :device nil
  8.3005-                   :defaults pathname ;; host device, and on scl, *some*
  8.3006-                   ;; scheme-specific parts: port username password, not others:
  8.3007-                   . #.(or #+scl '(:parameters nil :query nil :fragment nil))))
  8.3008-
  8.3009-  (defun ensure-absolute-pathname (path &optional defaults (on-error 'error))
  8.3010-    "Given a pathname designator PATH, return an absolute pathname as specified by PATH
  8.3011-considering the DEFAULTS, or, if not possible, use CALL-FUNCTION on the specified ON-ERROR behavior,
  8.3012-with a format control-string and other arguments as arguments"
  8.3013-    (cond
  8.3014-      ((absolute-pathname-p path))
  8.3015-      ((stringp path) (ensure-absolute-pathname (pathname path) defaults on-error))
  8.3016-      ((not (pathnamep path)) (call-function on-error "not a valid pathname designator ~S" path))
  8.3017-      ((let ((default-pathname (if (pathnamep defaults) defaults (call-function defaults))))
  8.3018-         (or (if (absolute-pathname-p default-pathname)
  8.3019-                 (absolute-pathname-p (merge-pathnames* path default-pathname))
  8.3020-                 (call-function on-error "Default pathname ~S is not an absolute pathname"
  8.3021-                                default-pathname))
  8.3022-             (call-function on-error "Failed to merge ~S with ~S into an absolute pathname"
  8.3023-                            path default-pathname))))
  8.3024-      (t (call-function on-error
  8.3025-                        "Cannot ensure ~S is evaluated as an absolute pathname with defaults ~S"
  8.3026-                        path defaults))))
  8.3027-
  8.3028-  (defun subpathp (maybe-subpath base-pathname)
  8.3029-    "if MAYBE-SUBPATH is a pathname that is under BASE-PATHNAME, return a pathname object that
  8.3030-when used with MERGE-PATHNAMES* with defaults BASE-PATHNAME, returns MAYBE-SUBPATH."
  8.3031-    (and (pathnamep maybe-subpath) (pathnamep base-pathname)
  8.3032-         (absolute-pathname-p maybe-subpath) (absolute-pathname-p base-pathname)
  8.3033-         (directory-pathname-p base-pathname) (not (wild-pathname-p base-pathname))
  8.3034-         (pathname-equal (pathname-root maybe-subpath) (pathname-root base-pathname))
  8.3035-         (with-pathname-defaults (*nil-pathname*)
  8.3036-           (let ((enough (enough-namestring maybe-subpath base-pathname)))
  8.3037-             (and (relative-pathname-p enough) (pathname enough))))))
  8.3038-
  8.3039-  (defun enough-pathname (maybe-subpath base-pathname)
  8.3040-    "if MAYBE-SUBPATH is a pathname that is under BASE-PATHNAME, return a pathname object that
  8.3041-when used with MERGE-PATHNAMES* with defaults BASE-PATHNAME, returns MAYBE-SUBPATH."
  8.3042-    (let ((sub (when maybe-subpath (pathname maybe-subpath)))
  8.3043-          (base (when base-pathname (ensure-absolute-pathname (pathname base-pathname)))))
  8.3044-      (or (and base (subpathp sub base)) sub)))
  8.3045-
  8.3046-  (defun call-with-enough-pathname (maybe-subpath defaults-pathname thunk)
  8.3047-    "In a context where *DEFAULT-PATHNAME-DEFAULTS* is bound to DEFAULTS-PATHNAME (if not null,
  8.3048-or else to its current value), call THUNK with ENOUGH-PATHNAME for MAYBE-SUBPATH
  8.3049-given DEFAULTS-PATHNAME as a base pathname."
  8.3050-    (let ((enough (enough-pathname maybe-subpath defaults-pathname))
  8.3051-          (*default-pathname-defaults* (or defaults-pathname *default-pathname-defaults*)))
  8.3052-      (funcall thunk enough)))
  8.3053-
  8.3054-  (defmacro with-enough-pathname ((pathname-var &key (pathname pathname-var)
  8.3055-                                                  (defaults *default-pathname-defaults*))
  8.3056-                                  &body body)
  8.3057-    "Shorthand syntax for CALL-WITH-ENOUGH-PATHNAME"
  8.3058-    `(call-with-enough-pathname ,pathname ,defaults #'(lambda (,pathname-var) ,@body))))
  8.3059-
  8.3060-
  8.3061-;;; Wildcard pathnames
  8.3062-(with-upgradability ()
  8.3063-  (defparameter *wild* (or #+cormanlisp "*" :wild)
  8.3064-    "Wild component for use with MAKE-PATHNAME")
  8.3065-  (defparameter *wild-directory-component* (or :wild)
  8.3066-    "Wild directory component for use with MAKE-PATHNAME")
  8.3067-  (defparameter *wild-inferiors-component* (or :wild-inferiors)
  8.3068-    "Wild-inferiors directory component for use with MAKE-PATHNAME")
  8.3069-  (defparameter *wild-file*
  8.3070-    (make-pathname :directory nil :name *wild* :type *wild*
  8.3071-                   :version (or #-(or allegro abcl xcl) *wild*))
  8.3072-    "A pathname object with wildcards for matching any file with TRANSLATE-PATHNAME")
  8.3073-  (defparameter *wild-file-for-directory*
  8.3074-    (make-pathname :directory nil :name *wild* :type (or #-(or clisp gcl) *wild*)
  8.3075-                   :version (or #-(or allegro abcl clisp gcl xcl) *wild*))
  8.3076-    "A pathname object with wildcards for matching any file with DIRECTORY")
  8.3077-  (defparameter *wild-directory*
  8.3078-    (make-pathname :directory `(:relative ,*wild-directory-component*)
  8.3079-                   :name nil :type nil :version nil)
  8.3080-    "A pathname object with wildcards for matching any subdirectory")
  8.3081-  (defparameter *wild-inferiors*
  8.3082-    (make-pathname :directory `(:relative ,*wild-inferiors-component*)
  8.3083-                   :name nil :type nil :version nil)
  8.3084-    "A pathname object with wildcards for matching any recursive subdirectory")
  8.3085-  (defparameter *wild-path*
  8.3086-    (merge-pathnames* *wild-file* *wild-inferiors*)
  8.3087-    "A pathname object with wildcards for matching any file in any recursive subdirectory")
  8.3088-
  8.3089-  (defun wilden (path)
  8.3090-    "From a pathname, return a wildcard pathname matching any file in any subdirectory of given pathname's directory"
  8.3091-    (merge-pathnames* *wild-path* path)))
  8.3092-
  8.3093-
  8.3094-;;; Translate a pathname
  8.3095-(with-upgradability ()
  8.3096-  (defun relativize-directory-component (directory-component)
  8.3097-    "Given the DIRECTORY-COMPONENT of a pathname, return an otherwise similar relative directory component"
  8.3098-    (let ((directory (normalize-pathname-directory-component directory-component)))
  8.3099-      (cond
  8.3100-        ((stringp directory)
  8.3101-         (list :relative directory))
  8.3102-        ((eq (car directory) :absolute)
  8.3103-         (cons :relative (cdr directory)))
  8.3104-        (t
  8.3105-         directory))))
  8.3106-
  8.3107-  (defun relativize-pathname-directory (pathspec)
  8.3108-    "Given a PATHNAME, return a relative pathname with otherwise the same components"
  8.3109-    (let ((p (pathname pathspec)))
  8.3110-      (make-pathname
  8.3111-       :directory (relativize-directory-component (pathname-directory p))
  8.3112-       :defaults p)))
  8.3113-
  8.3114-  (defun directory-separator-for-host (&optional (pathname *default-pathname-defaults*))
  8.3115-    "Given a PATHNAME, return the character used to delimit directory names on this host and device."
  8.3116-    (let ((foo (make-pathname :directory '(:absolute "FOO") :defaults pathname)))
  8.3117-      (last-char (namestring foo))))
  8.3118-
  8.3119-  #-scl
  8.3120-  (defun directorize-pathname-host-device (pathname)
  8.3121-    "Given a PATHNAME, return a pathname that has representations of its HOST and DEVICE components
  8.3122-added to its DIRECTORY component. This is useful for output translations."
  8.3123-    (os-cond
  8.3124-     ((os-unix-p)
  8.3125-      (when (physical-pathname-p pathname)
  8.3126-        (return-from directorize-pathname-host-device pathname))))
  8.3127-    (let* ((root (pathname-root pathname))
  8.3128-           (wild-root (wilden root))
  8.3129-           (absolute-pathname (merge-pathnames* pathname root))
  8.3130-           (separator (directory-separator-for-host root))
  8.3131-           (root-namestring (namestring root))
  8.3132-           (root-string
  8.3133-             (substitute-if #\/
  8.3134-                            #'(lambda (x) (or (eql x #\:)
  8.3135-                                              (eql x separator)))
  8.3136-                            root-namestring)))
  8.3137-      (multiple-value-bind (relative path filename)
  8.3138-          (split-unix-namestring-directory-components root-string :ensure-directory t)
  8.3139-        (declare (ignore relative filename))
  8.3140-        (let ((new-base (make-pathname :defaults root :directory `(:absolute ,@path))))
  8.3141-          (translate-pathname absolute-pathname wild-root (wilden new-base))))))
  8.3142-
  8.3143-  #+scl
  8.3144-  (defun directorize-pathname-host-device (pathname)
  8.3145-    (let ((scheme (ext:pathname-scheme pathname))
  8.3146-          (host (pathname-host pathname))
  8.3147-          (port (ext:pathname-port pathname))
  8.3148-          (directory (pathname-directory pathname)))
  8.3149-      (flet ((specificp (x) (and x (not (eq x :unspecific)))))
  8.3150-        (if (or (specificp port)
  8.3151-                (and (specificp host) (plusp (length host)))
  8.3152-                (specificp scheme))
  8.3153-            (let ((prefix ""))
  8.3154-              (when (specificp port)
  8.3155-                (setf prefix (format nil ":~D" port)))
  8.3156-              (when (and (specificp host) (plusp (length host)))
  8.3157-                (setf prefix (strcat host prefix)))
  8.3158-              (setf prefix (strcat ":" prefix))
  8.3159-              (when (specificp scheme)
  8.3160-                (setf prefix (strcat scheme prefix)))
  8.3161-              (assert (and directory (eq (first directory) :absolute)))
  8.3162-              (make-pathname :directory `(:absolute ,prefix ,@(rest directory))
  8.3163-                             :defaults pathname)))
  8.3164-        pathname)))
  8.3165-
  8.3166-  (defun translate-pathname* (path absolute-source destination &optional root source)
  8.3167-    "A wrapper around TRANSLATE-PATHNAME to be used by the ASDF output-translations facility.
  8.3168-PATH is the pathname to be translated.
  8.3169-ABSOLUTE-SOURCE is an absolute pathname to use as source for translate-pathname,
  8.3170-DESTINATION is either a function, to be called with PATH and ABSOLUTE-SOURCE,
  8.3171-or a relative pathname, to be merged with ROOT and used as destination for translate-pathname
  8.3172-or an absolute pathname, to be used as destination for translate-pathname.
  8.3173-In that last case, if ROOT is non-NIL, PATH is first transformated by DIRECTORIZE-PATHNAME-HOST-DEVICE."
  8.3174-    (declare (ignore source))
  8.3175-    (cond
  8.3176-      ((functionp destination)
  8.3177-       (funcall destination path absolute-source))
  8.3178-      ((eq destination t)
  8.3179-       path)
  8.3180-      ((not (pathnamep destination))
  8.3181-       (parameter-error "~S: Invalid destination" 'translate-pathname*))
  8.3182-      ((not (absolute-pathname-p destination))
  8.3183-       (translate-pathname path absolute-source (merge-pathnames* destination root)))
  8.3184-      (root
  8.3185-       (translate-pathname (directorize-pathname-host-device path) absolute-source destination))
  8.3186-      (t
  8.3187-       (translate-pathname path absolute-source destination))))
  8.3188-
  8.3189-  (defvar *output-translation-function* 'identity
  8.3190-    "Hook for output translations.
  8.3191-
  8.3192-This function needs to be idempotent, so that actions can work
  8.3193-whether their inputs were translated or not,
  8.3194-which they will be if we are composing operations. e.g. if some
  8.3195-create-lisp-op creates a lisp file from some higher-level input,
  8.3196-you need to still be able to use compile-op on that lisp file."))
  8.3197-;;;; -------------------------------------------------------------------------
  8.3198-;;;; Portability layer around Common Lisp filesystem access
  8.3199-
  8.3200-(uiop/package:define-package :uiop/filesystem
  8.3201-  (:use :uiop/common-lisp :uiop/package :uiop/utility :uiop/os :uiop/pathname)
  8.3202-  (:export
  8.3203-   ;; Native namestrings
  8.3204-   #:native-namestring #:parse-native-namestring
  8.3205-   ;; Probing the filesystem
  8.3206-   #:truename* #:safe-file-write-date #:probe-file* #:directory-exists-p #:file-exists-p
  8.3207-   #:directory* #:filter-logical-directory-results #:directory-files #:subdirectories
  8.3208-   #:collect-sub*directories
  8.3209-   ;; Resolving symlinks somewhat
  8.3210-   #:truenamize #:resolve-symlinks #:*resolve-symlinks* #:resolve-symlinks*
  8.3211-   ;; merging with cwd
  8.3212-   #:get-pathname-defaults #:call-with-current-directory #:with-current-directory
  8.3213-   ;; Environment pathnames
  8.3214-   #:inter-directory-separator #:split-native-pathnames-string
  8.3215-   #:getenv-pathname #:getenv-pathnames
  8.3216-   #:getenv-absolute-directory #:getenv-absolute-directories
  8.3217-   #:lisp-implementation-directory #:lisp-implementation-pathname-p
  8.3218-   ;; Simple filesystem operations
  8.3219-   #:ensure-all-directories-exist
  8.3220-   #:rename-file-overwriting-target
  8.3221-   #:delete-file-if-exists #:delete-empty-directory #:delete-directory-tree))
  8.3222-(in-package :uiop/filesystem)
  8.3223-
  8.3224-;;; Native namestrings, as seen by the operating system calls rather than Lisp
  8.3225-(with-upgradability ()
  8.3226-  (defun native-namestring (x)
  8.3227-    "From a non-wildcard CL pathname, a return namestring suitable for passing to the operating system"
  8.3228-    (when x
  8.3229-      (let ((p (pathname x)))
  8.3230-        #+clozure (with-pathname-defaults () (ccl:native-translated-namestring p)) ; see ccl bug 978
  8.3231-        #+(or cmucl scl) (ext:unix-namestring p nil)
  8.3232-        #+sbcl (sb-ext:native-namestring p)
  8.3233-        #-(or clozure cmucl sbcl scl)
  8.3234-        (os-cond
  8.3235-         ((os-unix-p) (unix-namestring p))
  8.3236-         (t (namestring p))))))
  8.3237-
  8.3238-  (defun parse-native-namestring (string &rest constraints &key ensure-directory &allow-other-keys)
  8.3239-    "From a native namestring suitable for use by the operating system, return
  8.3240-a CL pathname satisfying all the specified constraints as per ENSURE-PATHNAME"
  8.3241-    (check-type string (or string null))
  8.3242-    (let* ((pathname
  8.3243-             (when string
  8.3244-               (with-pathname-defaults ()
  8.3245-                 #+clozure (ccl:native-to-pathname string)
  8.3246-                 #+cmucl (uiop/os::parse-unix-namestring* string)
  8.3247-                 #+sbcl (sb-ext:parse-native-namestring string)
  8.3248-                 #+scl (lisp::parse-unix-namestring string)
  8.3249-                 #-(or clozure cmucl sbcl scl)
  8.3250-                 (os-cond
  8.3251-                  ((os-unix-p) (parse-unix-namestring string :ensure-directory ensure-directory))
  8.3252-                  (t (parse-namestring string))))))
  8.3253-           (pathname
  8.3254-             (if ensure-directory
  8.3255-                 (and pathname (ensure-directory-pathname pathname))
  8.3256-                 pathname)))
  8.3257-      (apply 'ensure-pathname pathname constraints))))
  8.3258-
  8.3259-
  8.3260-;;; Probing the filesystem
  8.3261-(with-upgradability ()
  8.3262-  (defun truename* (p)
  8.3263-    "Nicer variant of TRUENAME that plays well with NIL, avoids logical pathname contexts, and tries both files and directories"
  8.3264-    (when p
  8.3265-      (when (stringp p) (setf p (with-pathname-defaults () (parse-namestring p))))
  8.3266-      (values
  8.3267-       (or (ignore-errors (truename p))
  8.3268-           ;; this is here because trying to find the truename of a directory pathname WITHOUT supplying
  8.3269-           ;; a trailing directory separator, causes an error on some lisps.
  8.3270-           #+(or clisp gcl) (if-let (d (ensure-directory-pathname p nil)) (ignore-errors (truename d)))
  8.3271-           ;; On Genera, truename of a directory pathname will probably fail as Genera
  8.3272-           ;; will merge in a filename/type/version from *default-pathname-defaults* and
  8.3273-           ;; will try to get the truename of a file that probably doesn't exist.
  8.3274-           #+genera (when (directory-pathname-p p)
  8.3275-                      (let ((d (scl:send p :directory-pathname-as-file)))
  8.3276-                        (ensure-directory-pathname (ignore-errors (truename d)) nil)))))))
  8.3277-
  8.3278-  (defun safe-file-write-date (pathname)
  8.3279-    "Safe variant of FILE-WRITE-DATE that may return NIL rather than raise an error."
  8.3280-    ;; If FILE-WRITE-DATE returns NIL, it's possible that
  8.3281-    ;; the user or some other agent has deleted an input file.
  8.3282-    ;; Also, generated files will not exist at the time planning is done
  8.3283-    ;; and calls compute-action-stamp which calls safe-file-write-date.
  8.3284-    ;; So it is very possible that we can't get a valid file-write-date,
  8.3285-    ;; and we can survive and we will continue the planning
  8.3286-    ;; as if the file were very old.
  8.3287-    ;; (or should we treat the case in a different, special way?)
  8.3288-    (and pathname
  8.3289-         (handler-case (file-write-date (physicalize-pathname pathname))
  8.3290-           (file-error () nil))))
  8.3291-
  8.3292-  (defun probe-file* (p &key truename)
  8.3293-    "when given a pathname P (designated by a string as per PARSE-NAMESTRING),
  8.3294-probes the filesystem for a file or directory with given pathname.
  8.3295-If it exists, return its truename if TRUENAME is true,
  8.3296-or the original (parsed) pathname if it is false (the default)."
  8.3297-    (values
  8.3298-     (ignore-errors
  8.3299-      (setf p (funcall 'ensure-pathname p
  8.3300-                       :namestring :lisp
  8.3301-                       :ensure-physical t
  8.3302-                       :ensure-absolute t :defaults 'get-pathname-defaults
  8.3303-                       :want-non-wild t
  8.3304-                       :on-error nil))
  8.3305-      (when p
  8.3306-        #+allegro
  8.3307-        (probe-file p :follow-symlinks truename)
  8.3308-        #+gcl
  8.3309-        (if truename
  8.3310-            (truename* p)
  8.3311-            (let ((kind (car (si::stat p))))
  8.3312-              (when (eq kind :link)
  8.3313-                (setf kind (ignore-errors (car (si::stat (truename* p))))))
  8.3314-              (ecase kind
  8.3315-                ((nil) nil)
  8.3316-                ((:file :link)
  8.3317-                 (cond
  8.3318-                   ((file-pathname-p p) p)
  8.3319-                   ((directory-pathname-p p)
  8.3320-                    (subpathname p (car (last (pathname-directory p)))))))
  8.3321-                (:directory (ensure-directory-pathname p)))))
  8.3322-        #+clisp
  8.3323-        #.(let* ((fs (or #-os-windows (find-symbol* '#:file-stat :posix nil)))
  8.3324-                 (pp (find-symbol* '#:probe-pathname :ext nil)))
  8.3325-            `(if truename
  8.3326-                 ,(if pp
  8.3327-                      `(values (,pp p))
  8.3328-                      '(or (truename* p)
  8.3329-                        (truename* (ignore-errors (ensure-directory-pathname p)))))
  8.3330-                 ,(cond
  8.3331-                    (fs `(and (,fs p) p))
  8.3332-                    (pp `(nth-value 1 (,pp p)))
  8.3333-                    (t '(or (and (truename* p) p)
  8.3334-                         (if-let (d (ensure-directory-pathname p))
  8.3335-                          (and (truename* d) d)))))))
  8.3336-        #-(or allegro clisp gcl)
  8.3337-        (if truename
  8.3338-            (probe-file p)
  8.3339-            (and
  8.3340-             #+(or cmucl scl) (unix:unix-stat (ext:unix-namestring p))
  8.3341-             #+(and lispworks os-unix) (system:get-file-stat p)
  8.3342-             #+sbcl (sb-unix:unix-stat (sb-ext:native-namestring p))
  8.3343-             #-(or cmucl (and lispworks os-unix) sbcl scl) (file-write-date p)
  8.3344-             p))))))
  8.3345-
  8.3346-  (defun directory-exists-p (x)
  8.3347-    "Is X the name of a directory that exists on the filesystem?"
  8.3348-    #+allegro
  8.3349-    (excl:probe-directory x)
  8.3350-    #+clisp
  8.3351-    (handler-case (ext:probe-directory x)
  8.3352-           (sys::simple-file-error ()
  8.3353-             nil))
  8.3354-    #-(or allegro clisp)
  8.3355-    (let ((p (probe-file* x :truename t)))
  8.3356-      (and (directory-pathname-p p) p)))
  8.3357-
  8.3358-  (defun file-exists-p (x)
  8.3359-    "Is X the name of a file that exists on the filesystem?"
  8.3360-    (let ((p (probe-file* x :truename t)))
  8.3361-      (and (file-pathname-p p) p)))
  8.3362-
  8.3363-  (defun directory* (pathname-spec &rest keys &key &allow-other-keys)
  8.3364-    "Return a list of the entries in a directory by calling DIRECTORY.
  8.3365-Try to override the defaults to not resolving symlinks, if implementation allows."
  8.3366-    (apply 'directory pathname-spec
  8.3367-           (append keys '#.(or #+allegro '(:directories-are-files nil :follow-symbolic-links nil)
  8.3368-                               #+(or clozure digitool) '(:follow-links nil)
  8.3369-                               #+clisp '(:circle t :if-does-not-exist :ignore)
  8.3370-                               #+(or cmucl scl) '(:follow-links nil :truenamep nil)
  8.3371-                               #+lispworks '(:link-transparency nil)
  8.3372-                               #+sbcl (when (find-symbol* :resolve-symlinks '#:sb-impl nil)
  8.3373-                                        '(:resolve-symlinks nil))))))
  8.3374-
  8.3375-  (defun filter-logical-directory-results (directory entries merger)
  8.3376-    "If DIRECTORY isn't a logical pathname, return ENTRIES. If it is,
  8.3377-given ENTRIES in the DIRECTORY, remove the entries which are physical yet
  8.3378-when transformed by MERGER have a different TRUENAME.
  8.3379-Also remove duplicates as may appear with some translation rules.
  8.3380-This function is used as a helper to DIRECTORY-FILES to avoid invalid entries
  8.3381-when using logical-pathnames."
  8.3382-    (if (logical-pathname-p directory)
  8.3383-        (remove-duplicates ;; on CLISP, querying ~/ will return duplicates
  8.3384-         ;; Try hard to not resolve logical-pathname into physical pathnames;
  8.3385-         ;; otherwise logical-pathname users/lovers will be disappointed.
  8.3386-         ;; If directory* could use some implementation-dependent magic,
  8.3387-         ;; we will have logical pathnames already; otherwise,
  8.3388-         ;; we only keep pathnames for which specifying the name and
  8.3389-         ;; translating the LPN commute.
  8.3390-         (loop :for f :in entries
  8.3391-               :for p = (or (and (logical-pathname-p f) f)
  8.3392-                            (let* ((u (ignore-errors (call-function merger f))))
  8.3393-                              ;; The first u avoids a cumbersome (truename u) error.
  8.3394-                              ;; At this point f should already be a truename,
  8.3395-                              ;; but isn't quite in CLISP, for it doesn't have :version :newest
  8.3396-                              (and u (equal (truename* u) (truename* f)) u)))
  8.3397-           :when p :collect p)
  8.3398-         :test 'pathname-equal)
  8.3399-        entries))
  8.3400-
  8.3401-  (defun directory-files (directory &optional (pattern *wild-file-for-directory*))
  8.3402-    "Return a list of the files in a directory according to the PATTERN.
  8.3403-Subdirectories should NOT be returned.
  8.3404-  PATTERN defaults to a pattern carefully chosen based on the implementation;
  8.3405-override the default at your own risk.
  8.3406-  DIRECTORY-FILES tries NOT to resolve symlinks if the implementation permits this,
  8.3407-but the behavior in presence of symlinks is not portable. Use IOlib to handle such situations."
  8.3408-    (let ((dir (ensure-directory-pathname directory)))
  8.3409-      (when (logical-pathname-p dir)
  8.3410-        ;; Because of the filtering we do below,
  8.3411-        ;; logical pathnames have restrictions on wild patterns.
  8.3412-        ;; Not that the results are very portable when you use these patterns on physical pathnames.
  8.3413-        (when (wild-pathname-p dir)
  8.3414-          (parameter-error "~S: Invalid wild pattern in logical directory ~S"
  8.3415-                           'directory-files directory))
  8.3416-        (unless (member (pathname-directory pattern) '(() (:relative)) :test 'equal)
  8.3417-          (parameter-error "~S: Invalid file pattern ~S for logical directory ~S" 'directory-files pattern directory))
  8.3418-        (setf pattern (make-pathname-logical pattern (pathname-host dir))))
  8.3419-      (let* ((pat (merge-pathnames* pattern dir))
  8.3420-             (entries (ignore-errors (directory* pat))))
  8.3421-        (remove-if 'directory-pathname-p
  8.3422-                   (filter-logical-directory-results
  8.3423-                    directory entries
  8.3424-                    #'(lambda (f)
  8.3425-                        (make-pathname :defaults dir
  8.3426-                                       :name (make-pathname-component-logical (pathname-name f))
  8.3427-                                       :type (make-pathname-component-logical (pathname-type f))
  8.3428-                                       :version (make-pathname-component-logical (pathname-version f)))))))))
  8.3429-
  8.3430-  (defun subdirectories (directory)
  8.3431-    "Given a DIRECTORY pathname designator, return a list of the subdirectories under it.
  8.3432-The behavior in presence of symlinks is not portable. Use IOlib to handle such situations."
  8.3433-    (let* ((directory (ensure-directory-pathname directory))
  8.3434-           #-(or abcl cormanlisp genera xcl)
  8.3435-           (wild (merge-pathnames*
  8.3436-                  #-(or abcl allegro cmucl lispworks sbcl scl xcl)
  8.3437-                  *wild-directory*
  8.3438-                  #+(or abcl allegro cmucl lispworks sbcl scl xcl) "*.*"
  8.3439-                  directory))
  8.3440-           (dirs
  8.3441-             #-(or abcl cormanlisp genera xcl)
  8.3442-             (ignore-errors
  8.3443-              (directory* wild . #.(or #+clozure '(:directories t :files nil)
  8.3444-                                       #+mcl '(:directories t))))
  8.3445-             #+(or abcl xcl) (system:list-directory directory)
  8.3446-             #+cormanlisp (cl::directory-subdirs directory)
  8.3447-             #+genera (handler-case (fs:directory-list directory) (fs:directory-not-found () nil)))
  8.3448-           #+(or abcl allegro cmucl genera lispworks sbcl scl xcl)
  8.3449-           (dirs (loop :for x :in dirs
  8.3450-                       :for d = #+(or abcl xcl) (extensions:probe-directory x)
  8.3451-                       #+allegro (excl:probe-directory x)
  8.3452-                       #+(or cmucl sbcl scl) (directory-pathname-p x)
  8.3453-                       #+genera (getf (cdr x) :directory)
  8.3454-                       #+lispworks (lw:file-directory-p x)
  8.3455-                       :when d :collect #+(or abcl allegro xcl) (ensure-directory-pathname d)
  8.3456-                         #+genera (ensure-directory-pathname (first x))
  8.3457-                       #+(or cmucl lispworks sbcl scl) x)))
  8.3458-      (filter-logical-directory-results
  8.3459-       directory dirs
  8.3460-       (let ((prefix (or (normalize-pathname-directory-component (pathname-directory directory))
  8.3461-                         '(:absolute)))) ; because allegro returns NIL for #p"FOO:"
  8.3462-         #'(lambda (d)
  8.3463-             (let ((dir (normalize-pathname-directory-component (pathname-directory d))))
  8.3464-               (and (consp dir) (consp (cdr dir))
  8.3465-                    (make-pathname
  8.3466-                     :defaults directory :name nil :type nil :version nil
  8.3467-                     :directory (append prefix (make-pathname-component-logical (last dir)))))))))))
  8.3468-
  8.3469-  (defun collect-sub*directories (directory collectp recursep collector)
  8.3470-    "Given a DIRECTORY, when COLLECTP returns true when CALL-FUNCTION'ed with the directory,
  8.3471-call-function the COLLECTOR function designator on the directory,
  8.3472-and recurse each of its subdirectories on which the RECURSEP returns true when CALL-FUNCTION'ed with them.
  8.3473-This function will thus let you traverse a filesystem hierarchy,
  8.3474-superseding the functionality of CL-FAD:WALK-DIRECTORY.
  8.3475-The behavior in presence of symlinks is not portable. Use IOlib to handle such situations."
  8.3476-    (when (call-function collectp directory)
  8.3477-      (call-function collector directory)
  8.3478-      (dolist (subdir (subdirectories directory))
  8.3479-        (when (call-function recursep subdir)
  8.3480-          (collect-sub*directories subdir collectp recursep collector))))))
  8.3481-
  8.3482-;;; Resolving symlinks somewhat
  8.3483-(with-upgradability ()
  8.3484-  (defun truenamize (pathname)
  8.3485-    "Resolve as much of a pathname as possible"
  8.3486-    (block nil
  8.3487-      (when (typep pathname '(or null logical-pathname)) (return pathname))
  8.3488-      (let ((p pathname))
  8.3489-        (unless (absolute-pathname-p p)
  8.3490-          (setf p (or (absolute-pathname-p (ensure-absolute-pathname p 'get-pathname-defaults nil))
  8.3491-                      (return p))))
  8.3492-        (when (logical-pathname-p p) (return p))
  8.3493-        (let ((found (probe-file* p :truename t)))
  8.3494-          (when found (return found)))
  8.3495-        (let* ((directory (normalize-pathname-directory-component (pathname-directory p)))
  8.3496-               (up-components (reverse (rest directory)))
  8.3497-               (down-components ()))
  8.3498-          (assert (eq :absolute (first directory)))
  8.3499-          (loop :while up-components :do
  8.3500-            (if-let (parent
  8.3501-                     (ignore-errors
  8.3502-                      (probe-file* (make-pathname :directory `(:absolute ,@(reverse up-components))
  8.3503-                                                  :name nil :type nil :version nil :defaults p))))
  8.3504-              (if-let (simplified
  8.3505-                       (ignore-errors
  8.3506-                        (merge-pathnames*
  8.3507-                         (make-pathname :directory `(:relative ,@down-components)
  8.3508-                                        :defaults p)
  8.3509-                         (ensure-directory-pathname parent))))
  8.3510-                (return simplified)))
  8.3511-            (push (pop up-components) down-components)
  8.3512-            :finally (return p))))))
  8.3513-
  8.3514-  (defun resolve-symlinks (path)
  8.3515-    "Do a best effort at resolving symlinks in PATH, returning a partially or totally resolved PATH."
  8.3516-    #-allegro (truenamize path)
  8.3517-    #+allegro
  8.3518-    (if (physical-pathname-p path)
  8.3519-        (or (ignore-errors (excl:pathname-resolve-symbolic-links path)) path)
  8.3520-        path))
  8.3521-
  8.3522-  (defvar *resolve-symlinks* t
  8.3523-    "Determine whether or not ASDF resolves symlinks when defining systems.
  8.3524-Defaults to T.")
  8.3525-
  8.3526-  (defun resolve-symlinks* (path)
  8.3527-    "RESOLVE-SYMLINKS in PATH iff *RESOLVE-SYMLINKS* is T (the default)."
  8.3528-    (if *resolve-symlinks*
  8.3529-        (and path (resolve-symlinks path))
  8.3530-        path)))
  8.3531-
  8.3532-
  8.3533-;;; Check pathname constraints
  8.3534-(with-upgradability ()
  8.3535-  (defun ensure-pathname
  8.3536-      (pathname &key
  8.3537-                  on-error
  8.3538-                  defaults type dot-dot namestring
  8.3539-                  empty-is-nil
  8.3540-                  want-pathname
  8.3541-                  want-logical want-physical ensure-physical
  8.3542-                  want-relative want-absolute ensure-absolute ensure-subpath
  8.3543-                  want-non-wild want-wild wilden
  8.3544-                  want-file want-directory ensure-directory
  8.3545-                  want-existing ensure-directories-exist
  8.3546-                  truename resolve-symlinks truenamize
  8.3547-       &aux (p pathname)) ;; mutable working copy, preserve original
  8.3548-    "Coerces its argument into a PATHNAME,
  8.3549-optionally doing some transformations and checking specified constraints.
  8.3550-
  8.3551-If the argument is NIL, then NIL is returned unless the WANT-PATHNAME constraint is specified.
  8.3552-
  8.3553-If the argument is a STRING, it is first converted to a pathname via
  8.3554-PARSE-UNIX-NAMESTRING, PARSE-NAMESTRING or PARSE-NATIVE-NAMESTRING respectively
  8.3555-depending on the NAMESTRING argument being :UNIX, :LISP or :NATIVE respectively,
  8.3556-or else by using CALL-FUNCTION on the NAMESTRING argument;
  8.3557-if :UNIX is specified (or NIL, the default, which specifies the same thing),
  8.3558-then PARSE-UNIX-NAMESTRING it is called with the keywords
  8.3559-DEFAULTS TYPE DOT-DOT ENSURE-DIRECTORY WANT-RELATIVE, and
  8.3560-the result is optionally merged into the DEFAULTS if ENSURE-ABSOLUTE is true.
  8.3561-
  8.3562-The pathname passed or resulting from parsing the string
  8.3563-is then subjected to all the checks and transformations below are run.
  8.3564-
  8.3565-Each non-nil constraint argument can be one of the symbols T, ERROR, CERROR or IGNORE.
  8.3566-The boolean T is an alias for ERROR.
  8.3567-ERROR means that an error will be raised if the constraint is not satisfied.
  8.3568-CERROR means that an continuable error will be raised if the constraint is not satisfied.
  8.3569-IGNORE means just return NIL instead of the pathname.
  8.3570-
  8.3571-The ON-ERROR argument, if not NIL, is a function designator (as per CALL-FUNCTION)
  8.3572-that will be called with the the following arguments:
  8.3573-a generic format string for ensure pathname, the pathname,
  8.3574-the keyword argument corresponding to the failed check or transformation,
  8.3575-a format string for the reason ENSURE-PATHNAME failed,
  8.3576-and a list with arguments to that format string.
  8.3577-If ON-ERROR is NIL, ERROR is used instead, which does the right thing.
  8.3578-You could also pass (CERROR \"CONTINUE DESPITE FAILED CHECK\").
  8.3579-
  8.3580-The transformations and constraint checks are done in this order,
  8.3581-which is also the order in the lambda-list:
  8.3582-
  8.3583-EMPTY-IS-NIL returns NIL if the argument is an empty string.
  8.3584-WANT-PATHNAME checks that pathname (after parsing if needed) is not null.
  8.3585-Otherwise, if the pathname is NIL, ensure-pathname returns NIL.
  8.3586-WANT-LOGICAL checks that pathname is a LOGICAL-PATHNAME
  8.3587-WANT-PHYSICAL checks that pathname is not a LOGICAL-PATHNAME
  8.3588-ENSURE-PHYSICAL ensures that pathname is physical via TRANSLATE-LOGICAL-PATHNAME
  8.3589-WANT-RELATIVE checks that pathname has a relative directory component
  8.3590-WANT-ABSOLUTE checks that pathname does have an absolute directory component
  8.3591-ENSURE-ABSOLUTE merges with the DEFAULTS, then checks again
  8.3592-that the result absolute is an absolute pathname indeed.
  8.3593-ENSURE-SUBPATH checks that the pathname is a subpath of the DEFAULTS.
  8.3594-WANT-FILE checks that pathname has a non-nil FILE component
  8.3595-WANT-DIRECTORY checks that pathname has nil FILE and TYPE components
  8.3596-ENSURE-DIRECTORY uses ENSURE-DIRECTORY-PATHNAME to interpret
  8.3597-any file and type components as being actually a last directory component.
  8.3598-WANT-NON-WILD checks that pathname is not a wild pathname
  8.3599-WANT-WILD checks that pathname is a wild pathname
  8.3600-WILDEN merges the pathname with **/*.*.* if it is not wild
  8.3601-WANT-EXISTING checks that a file (or directory) exists with that pathname.
  8.3602-ENSURE-DIRECTORIES-EXIST creates any parent directory with ENSURE-DIRECTORIES-EXIST.
  8.3603-TRUENAME replaces the pathname by its truename, or errors if not possible.
  8.3604-RESOLVE-SYMLINKS replaces the pathname by a variant with symlinks resolved by RESOLVE-SYMLINKS.
  8.3605-TRUENAMIZE uses TRUENAMIZE to resolve as many symlinks as possible."
  8.3606-    (block nil
  8.3607-      (flet ((report-error (keyword description &rest arguments)
  8.3608-               (call-function (or on-error 'error)
  8.3609-                              "Invalid pathname ~S: ~*~?"
  8.3610-                              pathname keyword description arguments)))
  8.3611-        (macrolet ((err (constraint &rest arguments)
  8.3612-                     `(report-error ',(intern* constraint :keyword) ,@arguments))
  8.3613-                   (check (constraint condition &rest arguments)
  8.3614-                     `(when ,constraint
  8.3615-                        (unless ,condition (err ,constraint ,@arguments))))
  8.3616-                   (transform (transform condition expr)
  8.3617-                     `(when ,transform
  8.3618-                        (,@(if condition `(when ,condition) '(progn))
  8.3619-                         (setf p ,expr)))))
  8.3620-          (etypecase p
  8.3621-            ((or null pathname))
  8.3622-            (string
  8.3623-             (when (and (emptyp p) empty-is-nil)
  8.3624-               (return-from ensure-pathname nil))
  8.3625-             (setf p (case namestring
  8.3626-                       ((:unix nil)
  8.3627-                        (parse-unix-namestring
  8.3628-                         p :defaults defaults :type type :dot-dot dot-dot
  8.3629-                           :ensure-directory ensure-directory :want-relative want-relative))
  8.3630-                       ((:native)
  8.3631-                        (parse-native-namestring p))
  8.3632-                       ((:lisp)
  8.3633-                        (parse-namestring p))
  8.3634-                       (t
  8.3635-                        (call-function namestring p))))))
  8.3636-          (etypecase p
  8.3637-            (pathname)
  8.3638-            (null
  8.3639-             (check want-pathname (pathnamep p) "Expected a pathname, not NIL")
  8.3640-             (return nil)))
  8.3641-          (check want-logical (logical-pathname-p p) "Expected a logical pathname")
  8.3642-          (check want-physical (physical-pathname-p p) "Expected a physical pathname")
  8.3643-          (transform ensure-physical () (physicalize-pathname p))
  8.3644-          (check ensure-physical (physical-pathname-p p) "Could not translate to a physical pathname")
  8.3645-          (check want-relative (relative-pathname-p p) "Expected a relative pathname")
  8.3646-          (check want-absolute (absolute-pathname-p p) "Expected an absolute pathname")
  8.3647-          (transform ensure-absolute (not (absolute-pathname-p p))
  8.3648-                     (ensure-absolute-pathname p defaults (list #'report-error :ensure-absolute "~@?")))
  8.3649-          (check ensure-absolute (absolute-pathname-p p)
  8.3650-                 "Could not make into an absolute pathname even after merging with ~S" defaults)
  8.3651-          (check ensure-subpath (absolute-pathname-p defaults)
  8.3652-                 "cannot be checked to be a subpath of non-absolute pathname ~S" defaults)
  8.3653-          (check ensure-subpath (subpathp p defaults) "is not a sub pathname of ~S" defaults)
  8.3654-          (check want-file (file-pathname-p p) "Expected a file pathname")
  8.3655-          (check want-directory (directory-pathname-p p) "Expected a directory pathname")
  8.3656-          (transform ensure-directory (not (directory-pathname-p p)) (ensure-directory-pathname p))
  8.3657-          (check want-non-wild (not (wild-pathname-p p)) "Expected a non-wildcard pathname")
  8.3658-          (check want-wild (wild-pathname-p p) "Expected a wildcard pathname")
  8.3659-          (transform wilden (not (wild-pathname-p p)) (wilden p))
  8.3660-          (when want-existing
  8.3661-            (let ((existing (probe-file* p :truename truename)))
  8.3662-              (if existing
  8.3663-                  (when truename
  8.3664-                    (return existing))
  8.3665-                  (err want-existing "Expected an existing pathname"))))
  8.3666-          (when ensure-directories-exist (ensure-directories-exist p))
  8.3667-          (when truename
  8.3668-            (let ((truename (truename* p)))
  8.3669-              (if truename
  8.3670-                  (return truename)
  8.3671-                  (err truename "Can't get a truename for pathname"))))
  8.3672-          (transform resolve-symlinks () (resolve-symlinks p))
  8.3673-          (transform truenamize () (truenamize p))
  8.3674-          p)))))
  8.3675-
  8.3676-
  8.3677-;;; Pathname defaults
  8.3678-(with-upgradability ()
  8.3679-  (defun get-pathname-defaults (&optional (defaults *default-pathname-defaults*))
  8.3680-    "Find the actual DEFAULTS to use for pathnames, including
  8.3681-resolving them with respect to GETCWD if the DEFAULTS were relative"
  8.3682-    (or (absolute-pathname-p defaults)
  8.3683-        (merge-pathnames* defaults (getcwd))))
  8.3684-
  8.3685-  (defun call-with-current-directory (dir thunk)
  8.3686-    "call the THUNK in a context where the current directory was changed to DIR, if not NIL.
  8.3687-Note that this operation is usually NOT thread-safe."
  8.3688-    (if dir
  8.3689-        (let* ((dir (resolve-symlinks*
  8.3690-                     (get-pathname-defaults
  8.3691-                      (ensure-directory-pathname
  8.3692-                       dir))))
  8.3693-               (cwd (getcwd))
  8.3694-               (*default-pathname-defaults* dir))
  8.3695-          (chdir dir)
  8.3696-          (unwind-protect
  8.3697-               (funcall thunk)
  8.3698-            (chdir cwd)))
  8.3699-        (funcall thunk)))
  8.3700-
  8.3701-  (defmacro with-current-directory ((&optional dir) &body body)
  8.3702-    "Call BODY while the POSIX current working directory is set to DIR"
  8.3703-    `(call-with-current-directory ,dir #'(lambda () ,@body))))
  8.3704-
  8.3705-
  8.3706-;;; Environment pathnames
  8.3707-(with-upgradability ()
  8.3708-  (defun inter-directory-separator ()
  8.3709-    "What character does the current OS conventionally uses to separate directories?"
  8.3710-    (os-cond ((os-unix-p) #\:) (t #\;)))
  8.3711-
  8.3712-  (defun split-native-pathnames-string (string &rest constraints &key &allow-other-keys)
  8.3713-    "Given a string of pathnames specified in native OS syntax, separate them in a list,
  8.3714-check constraints and normalize each one as per ENSURE-PATHNAME,
  8.3715-where an empty string denotes NIL."
  8.3716-    (loop :for namestring :in (split-string string :separator (string (inter-directory-separator)))
  8.3717-          :collect (unless (emptyp namestring) (apply 'parse-native-namestring namestring constraints))))
  8.3718-
  8.3719-  (defun getenv-pathname (x &rest constraints &key ensure-directory want-directory on-error &allow-other-keys)
  8.3720-    "Extract a pathname from a user-configured environment variable, as per native OS,
  8.3721-check constraints and normalize as per ENSURE-PATHNAME."
  8.3722-    ;; For backward compatibility with ASDF 2, want-directory implies ensure-directory
  8.3723-    (apply 'parse-native-namestring (getenvp x)
  8.3724-           :ensure-directory (or ensure-directory want-directory)
  8.3725-           :on-error (or on-error
  8.3726-                         `(error "In (~S ~S), invalid pathname ~*~S: ~*~?" getenv-pathname ,x))
  8.3727-           constraints))
  8.3728-  (defun getenv-pathnames (x &rest constraints &key on-error &allow-other-keys)
  8.3729-    "Extract a list of pathname from a user-configured environment variable, as per native OS,
  8.3730-check constraints and normalize each one as per ENSURE-PATHNAME.
  8.3731-       Any empty entries in the environment variable X will be returned as NILs."
  8.3732-    (unless (getf constraints :empty-is-nil t)
  8.3733-      (parameter-error "Cannot have EMPTY-IS-NIL false for ~S" 'getenv-pathnames))
  8.3734-    (apply 'split-native-pathnames-string (getenvp x)
  8.3735-           :on-error (or on-error
  8.3736-                         `(error "In (~S ~S), invalid pathname ~*~S: ~*~?" getenv-pathnames ,x))
  8.3737-           :empty-is-nil t
  8.3738-           constraints))
  8.3739-  (defun getenv-absolute-directory (x)
  8.3740-    "Extract an absolute directory pathname from a user-configured environment variable,
  8.3741-as per native OS"
  8.3742-    (getenv-pathname x :want-absolute t :ensure-directory t))
  8.3743-  (defun getenv-absolute-directories (x)
  8.3744-    "Extract a list of absolute directories from a user-configured environment variable,
  8.3745-as per native OS.  Any empty entries in the environment variable X will be returned as
  8.3746-NILs."
  8.3747-    (getenv-pathnames x :want-absolute t :ensure-directory t))
  8.3748-
  8.3749-  (defun lisp-implementation-directory (&key truename)
  8.3750-    "Where are the system files of the current installation of the CL implementation?"
  8.3751-    (declare (ignorable truename))
  8.3752-    (let ((dir
  8.3753-            #+abcl extensions:*lisp-home*
  8.3754-            #+(or allegro clasp ecl mkcl) #p"SYS:"
  8.3755-            #+clisp custom:*lib-directory*
  8.3756-            #+clozure #p"ccl:"
  8.3757-            #+cmucl (ignore-errors (pathname-parent-directory-pathname (truename #p"modules:")))
  8.3758-            #+gcl system::*system-directory*
  8.3759-            #+lispworks lispworks:*lispworks-directory*
  8.3760-            #+sbcl (if-let (it (find-symbol* :sbcl-homedir-pathname :sb-int nil))
  8.3761-                     (funcall it)
  8.3762-                     (getenv-pathname "SBCL_HOME" :ensure-directory t))
  8.3763-            #+scl (ignore-errors (pathname-parent-directory-pathname (truename #p"file://modules/")))
  8.3764-            #+xcl ext:*xcl-home*))
  8.3765-      (if (and dir truename)
  8.3766-          (truename* dir)
  8.3767-          dir)))
  8.3768-
  8.3769-  (defun lisp-implementation-pathname-p (pathname)
  8.3770-    "Is the PATHNAME under the current installation of the CL implementation?"
  8.3771-    ;; Other builtin systems are those under the implementation directory
  8.3772-    (and (when pathname
  8.3773-           (if-let (impdir (lisp-implementation-directory))
  8.3774-             (or (subpathp pathname impdir)
  8.3775-                 (when *resolve-symlinks*
  8.3776-                   (if-let (truename (truename* pathname))
  8.3777-                     (if-let (trueimpdir (truename* impdir))
  8.3778-                       (subpathp truename trueimpdir)))))))
  8.3779-         t)))
  8.3780-
  8.3781-
  8.3782-;;; Simple filesystem operations
  8.3783-(with-upgradability ()
  8.3784-  (defun ensure-all-directories-exist (pathnames)
  8.3785-    "Ensure that for every pathname in PATHNAMES, we ensure its directories exist"
  8.3786-    (dolist (pathname pathnames)
  8.3787-      (when pathname
  8.3788-        (ensure-directories-exist (physicalize-pathname pathname)))))
  8.3789-
  8.3790-  (defun delete-file-if-exists (x)
  8.3791-    "Delete a file X if it already exists"
  8.3792-    (when x (handler-case (delete-file x) (file-error () nil))))
  8.3793-
  8.3794-  (defun rename-file-overwriting-target (source target)
  8.3795-    "Rename a file, overwriting any previous file with the TARGET name,
  8.3796-in an atomic way if the implementation allows."
  8.3797-    (let ((source (ensure-pathname source :namestring :lisp :ensure-physical t :want-file t))
  8.3798-          (target (ensure-pathname target :namestring :lisp :ensure-physical t :want-file t)))
  8.3799-      #+clisp ;; in recent enough versions of CLISP, :if-exists :overwrite would make it atomic
  8.3800-      (progn (funcall 'require "syscalls")
  8.3801-             (symbol-call :posix :copy-file source target :method :rename))
  8.3802-      #+(and sbcl os-windows) (delete-file-if-exists target) ;; not atomic
  8.3803-      #-clisp
  8.3804-      (rename-file source target
  8.3805-                   #+(or clasp clozure ecl) :if-exists
  8.3806-                   #+clozure :rename-and-delete #+(or clasp ecl) t)))
  8.3807-
  8.3808-  (defun delete-empty-directory (directory-pathname)
  8.3809-    "Delete an empty directory"
  8.3810-    #+(or abcl digitool gcl) (delete-file directory-pathname)
  8.3811-    #+allegro (excl:delete-directory directory-pathname)
  8.3812-    #+clisp (ext:delete-directory directory-pathname)
  8.3813-    #+clozure (ccl::delete-empty-directory directory-pathname)
  8.3814-    #+(or cmucl scl) (multiple-value-bind (ok errno)
  8.3815-                       (unix:unix-rmdir (native-namestring directory-pathname))
  8.3816-                     (unless ok
  8.3817-                       #+cmucl (error "Error number ~A when trying to delete directory ~A"
  8.3818-                                    errno directory-pathname)
  8.3819-                       #+scl (error "~@<Error deleting ~S: ~A~@:>"
  8.3820-                                    directory-pathname (unix:get-unix-error-msg errno))))
  8.3821-    #+cormanlisp (win32:delete-directory directory-pathname)
  8.3822-    #+(or clasp ecl) (si:rmdir directory-pathname)
  8.3823-    #+genera (fs:delete-directory directory-pathname)
  8.3824-    #+lispworks (lw:delete-directory directory-pathname)
  8.3825-    #+mkcl (mkcl:rmdir directory-pathname)
  8.3826-    #+sbcl #.(if-let (dd (find-symbol* :delete-directory :sb-ext nil))
  8.3827-               `(,dd directory-pathname) ;; requires SBCL 1.0.44 or later
  8.3828-               `(progn (require :sb-posix) (symbol-call :sb-posix :rmdir directory-pathname)))
  8.3829-    #+xcl (symbol-call :uiop :run-program `("rmdir" ,(native-namestring directory-pathname)))
  8.3830-    #-(or abcl allegro clasp clisp clozure cmucl cormanlisp digitool ecl gcl genera lispworks mkcl sbcl scl xcl)
  8.3831-    (not-implemented-error 'delete-empty-directory "(on your platform)")) ; genera
  8.3832-
  8.3833-  (defun delete-directory-tree (directory-pathname &key (validate nil validatep) (if-does-not-exist :error))
  8.3834-    "Delete a directory including all its recursive contents, aka rm -rf.
  8.3835-
  8.3836-To reduce the risk of infortunate mistakes, DIRECTORY-PATHNAME must be
  8.3837-a physical non-wildcard directory pathname (not namestring).
  8.3838-
  8.3839-If the directory does not exist, the IF-DOES-NOT-EXIST argument specifies what happens:
  8.3840-if it is :ERROR (the default), an error is signaled, whereas if it is :IGNORE, nothing is done.
  8.3841-
  8.3842-Furthermore, before any deletion is attempted, the DIRECTORY-PATHNAME must pass
  8.3843-the validation function designated (as per ENSURE-FUNCTION) by the VALIDATE keyword argument
  8.3844-which in practice is thus compulsory, and validates by returning a non-NIL result.
  8.3845-If you're suicidal or extremely confident, just use :VALIDATE T."
  8.3846-    (check-type if-does-not-exist (member :error :ignore))
  8.3847-    (setf directory-pathname (ensure-pathname directory-pathname
  8.3848-                                              :want-pathname t :want-non-wild t
  8.3849-                                              :want-physical t :want-directory t))
  8.3850-    (cond
  8.3851-      ((not validatep)
  8.3852-       (parameter-error "~S was asked to delete ~S but was not provided a validation predicate"
  8.3853-              'delete-directory-tree directory-pathname))
  8.3854-      ((not (call-function validate directory-pathname))
  8.3855-       (parameter-error "~S was asked to delete ~S but it is not valid ~@[according to ~S~]"
  8.3856-              'delete-directory-tree directory-pathname validate))
  8.3857-      ((not (directory-exists-p directory-pathname))
  8.3858-       (ecase if-does-not-exist
  8.3859-         (:error
  8.3860-          (error "~S was asked to delete ~S but the directory does not exist"
  8.3861-              'delete-directory-tree directory-pathname))
  8.3862-         (:ignore nil)))
  8.3863-      #-(or allegro cmucl clozure genera sbcl scl)
  8.3864-      ((os-unix-p) ;; On Unix, don't recursively walk the directory and delete everything in Lisp,
  8.3865-       ;; except on implementations where we can prevent DIRECTORY from following symlinks;
  8.3866-       ;; instead spawn a standard external program to do the dirty work.
  8.3867-       (symbol-call :uiop :run-program `("rm" "-rf" ,(native-namestring directory-pathname))))
  8.3868-      (t
  8.3869-       ;; On supported implementation, call supported system functions
  8.3870-       #+allegro (symbol-call :excl.osi :delete-directory-and-files
  8.3871-                              directory-pathname :if-does-not-exist if-does-not-exist)
  8.3872-       #+clozure (ccl:delete-directory directory-pathname)
  8.3873-       #+genera (fs:delete-directory directory-pathname :confirm nil)
  8.3874-       #+sbcl #.(if-let (dd (find-symbol* :delete-directory :sb-ext nil))
  8.3875-                  `(,dd directory-pathname :recursive t) ;; requires SBCL 1.0.44 or later
  8.3876-                  '(error "~S requires SBCL 1.0.44 or later" 'delete-directory-tree))
  8.3877-       ;; Outside Unix or on CMUCL and SCL that can avoid following symlinks,
  8.3878-       ;; do things the hard way.
  8.3879-       #-(or allegro clozure genera sbcl)
  8.3880-       (let ((sub*directories
  8.3881-               (while-collecting (c)
  8.3882-                 (collect-sub*directories directory-pathname t t #'c))))
  8.3883-             (dolist (d (nreverse sub*directories))
  8.3884-               (map () 'delete-file (directory-files d))
  8.3885-               (delete-empty-directory d)))))))
  8.3886-;;;; ---------------------------------------------------------------------------
  8.3887-;;;; Utilities related to streams
  8.3888-
  8.3889-(uiop/package:define-package :uiop/stream
  8.3890-  (:use :uiop/common-lisp :uiop/package :uiop/utility :uiop/os :uiop/pathname :uiop/filesystem)
  8.3891-  (:export
  8.3892-   #:*default-stream-element-type*
  8.3893-   #:*stdin* #:setup-stdin #:*stdout* #:setup-stdout #:*stderr* #:setup-stderr
  8.3894-   #:detect-encoding #:*encoding-detection-hook* #:always-default-encoding
  8.3895-   #:encoding-external-format #:*encoding-external-format-hook* #:default-encoding-external-format
  8.3896-   #:*default-encoding* #:*utf-8-external-format*
  8.3897-   #:with-safe-io-syntax #:call-with-safe-io-syntax #:safe-read-from-string
  8.3898-   #:with-output #:output-string #:with-input #:input-string
  8.3899-   #:with-input-file #:call-with-input-file #:with-output-file #:call-with-output-file
  8.3900-   #:null-device-pathname #:call-with-null-input #:with-null-input
  8.3901-   #:call-with-null-output #:with-null-output
  8.3902-   #:finish-outputs #:format! #:safe-format!
  8.3903-   #:copy-stream-to-stream #:concatenate-files #:copy-file
  8.3904-   #:slurp-stream-string #:slurp-stream-lines #:slurp-stream-line
  8.3905-   #:slurp-stream-forms #:slurp-stream-form
  8.3906-   #:read-file-string #:read-file-line #:read-file-lines #:safe-read-file-line
  8.3907-   #:read-file-forms #:read-file-form #:safe-read-file-form
  8.3908-   #:eval-input #:eval-thunk #:standard-eval-thunk
  8.3909-   #:println #:writeln
  8.3910-   #:file-stream-p #:file-or-synonym-stream-p
  8.3911-   ;; Temporary files
  8.3912-   #:*temporary-directory* #:temporary-directory #:default-temporary-directory
  8.3913-   #:setup-temporary-directory
  8.3914-   #:call-with-temporary-file #:with-temporary-file
  8.3915-   #:add-pathname-suffix #:tmpize-pathname
  8.3916-   #:call-with-staging-pathname #:with-staging-pathname))
  8.3917-(in-package :uiop/stream)
  8.3918-
  8.3919-(with-upgradability ()
  8.3920-  (defvar *default-stream-element-type*
  8.3921-    (or #+(or abcl cmucl cormanlisp scl xcl) 'character
  8.3922-        #+lispworks 'lw:simple-char
  8.3923-        :default)
  8.3924-    "default element-type for open (depends on the current CL implementation)")
  8.3925-
  8.3926-  (defvar *stdin* *standard-input*
  8.3927-    "the original standard input stream at startup")
  8.3928-
  8.3929-  (defun setup-stdin ()
  8.3930-    (setf *stdin*
  8.3931-          #.(or #+clozure 'ccl::*stdin*
  8.3932-                #+(or cmucl scl) 'system:*stdin*
  8.3933-                #+(or clasp ecl) 'ext::+process-standard-input+
  8.3934-                #+sbcl 'sb-sys:*stdin*
  8.3935-                '*standard-input*)))
  8.3936-
  8.3937-  (defvar *stdout* *standard-output*
  8.3938-    "the original standard output stream at startup")
  8.3939-
  8.3940-  (defun setup-stdout ()
  8.3941-    (setf *stdout*
  8.3942-          #.(or #+clozure 'ccl::*stdout*
  8.3943-                #+(or cmucl scl) 'system:*stdout*
  8.3944-                #+(or clasp ecl) 'ext::+process-standard-output+
  8.3945-                #+sbcl 'sb-sys:*stdout*
  8.3946-                '*standard-output*)))
  8.3947-
  8.3948-  (defvar *stderr* *error-output*
  8.3949-    "the original error output stream at startup")
  8.3950-
  8.3951-  (defun setup-stderr ()
  8.3952-    (setf *stderr*
  8.3953-          #.(or #+allegro 'excl::*stderr*
  8.3954-                #+clozure 'ccl::*stderr*
  8.3955-                #+(or cmucl scl) 'system:*stderr*
  8.3956-                #+(or clasp ecl) 'ext::+process-error-output+
  8.3957-                #+sbcl 'sb-sys:*stderr*
  8.3958-                '*error-output*)))
  8.3959-
  8.3960-  ;; Run them now. In image.lisp, we'll register them to be run at image restart.
  8.3961-  (setup-stdin) (setup-stdout) (setup-stderr))
  8.3962-
  8.3963-
  8.3964-;;; Encodings (mostly hooks only; full support requires asdf-encodings)
  8.3965-(with-upgradability ()
  8.3966-  (defparameter *default-encoding*
  8.3967-    ;; preserve explicit user changes to something other than the legacy default :default
  8.3968-    (or (if-let (previous (and (boundp '*default-encoding*) (symbol-value '*default-encoding*)))
  8.3969-          (unless (eq previous :default) previous))
  8.3970-        :utf-8)
  8.3971-    "Default encoding for source files.
  8.3972-The default value :utf-8 is the portable thing.
  8.3973-The legacy behavior was :default.
  8.3974-If you (asdf:load-system :asdf-encodings) then
  8.3975-you will have autodetection via *encoding-detection-hook* below,
  8.3976-reading emacs-style -*- coding: utf-8 -*- specifications,
  8.3977-and falling back to utf-8 or latin1 if nothing is specified.")
  8.3978-
  8.3979-  (defparameter *utf-8-external-format*
  8.3980-    (if (featurep :asdf-unicode)
  8.3981-        (or #+clisp charset:utf-8 :utf-8)
  8.3982-        :default)
  8.3983-    "Default :external-format argument to pass to CL:OPEN and also
  8.3984-CL:LOAD or CL:COMPILE-FILE to best process a UTF-8 encoded file.
  8.3985-On modern implementations, this will decode UTF-8 code points as CL characters.
  8.3986-On legacy implementations, it may fall back on some 8-bit encoding,
  8.3987-with non-ASCII code points being read as several CL characters;
  8.3988-hopefully, if done consistently, that won't affect program behavior too much.")
  8.3989-
  8.3990-  (defun always-default-encoding (pathname)
  8.3991-    "Trivial function to use as *encoding-detection-hook*,
  8.3992-always 'detects' the *default-encoding*"
  8.3993-    (declare (ignore pathname))
  8.3994-    *default-encoding*)
  8.3995-
  8.3996-  (defvar *encoding-detection-hook* #'always-default-encoding
  8.3997-    "Hook for an extension to define a function to automatically detect a file's encoding")
  8.3998-
  8.3999-  (defun detect-encoding (pathname)
  8.4000-    "Detects the encoding of a specified file, going through user-configurable hooks"
  8.4001-    (if (and pathname (not (directory-pathname-p pathname)) (probe-file* pathname))
  8.4002-        (funcall *encoding-detection-hook* pathname)
  8.4003-        *default-encoding*))
  8.4004-
  8.4005-  (defun default-encoding-external-format (encoding)
  8.4006-    "Default, ignorant, function to transform a character ENCODING as a
  8.4007-portable keyword to an implementation-dependent EXTERNAL-FORMAT specification.
  8.4008-Load system ASDF-ENCODINGS to hook in a better one."
  8.4009-    (case encoding
  8.4010-      (:default :default) ;; for backward-compatibility only. Explicit usage discouraged.
  8.4011-      (:utf-8 *utf-8-external-format*)
  8.4012-      (otherwise
  8.4013-       (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)
  8.4014-       :default)))
  8.4015-
  8.4016-  (defvar *encoding-external-format-hook*
  8.4017-    #'default-encoding-external-format
  8.4018-    "Hook for an extension (e.g. ASDF-ENCODINGS) to define a better mapping
  8.4019-from non-default encodings to and implementation-defined external-format's")
  8.4020-
  8.4021-  (defun encoding-external-format (encoding)
  8.4022-    "Transform a portable ENCODING keyword to an implementation-dependent EXTERNAL-FORMAT,
  8.4023-going through all the proper hooks."
  8.4024-    (funcall *encoding-external-format-hook* (or encoding *default-encoding*))))
  8.4025-
  8.4026-
  8.4027-;;; Safe syntax
  8.4028-(with-upgradability ()
  8.4029-  (defvar *standard-readtable* (with-standard-io-syntax *readtable*)
  8.4030-    "The standard readtable, implementing the syntax specified by the CLHS.
  8.4031-It must never be modified, though only good implementations will even enforce that.")
  8.4032-
  8.4033-  (defmacro with-safe-io-syntax ((&key (package :cl)) &body body)
  8.4034-    "Establish safe CL reader options around the evaluation of BODY"
  8.4035-    `(call-with-safe-io-syntax #'(lambda () (let ((*package* (find-package ,package))) ,@body))))
  8.4036-
  8.4037-  (defun call-with-safe-io-syntax (thunk &key (package :cl))
  8.4038-    (with-standard-io-syntax
  8.4039-      (let ((*package* (find-package package))
  8.4040-            (*read-default-float-format* 'double-float)
  8.4041-            (*print-readably* nil)
  8.4042-            (*read-eval* nil))
  8.4043-        (funcall thunk))))
  8.4044-
  8.4045-  (defun safe-read-from-string (string &key (package :cl) (eof-error-p t) eof-value (start 0) end preserve-whitespace)
  8.4046-    "Read from STRING using a safe syntax, as per WITH-SAFE-IO-SYNTAX"
  8.4047-    (with-safe-io-syntax (:package package)
  8.4048-      (read-from-string string eof-error-p eof-value :start start :end end :preserve-whitespace preserve-whitespace))))
  8.4049-
  8.4050-;;; Output helpers
  8.4051- (with-upgradability ()
  8.4052-  (defun call-with-output-file (pathname thunk
  8.4053-                                &key
  8.4054-                                  (element-type *default-stream-element-type*)
  8.4055-                                  (external-format *utf-8-external-format*)
  8.4056-                                  (if-exists :error)
  8.4057-                                  (if-does-not-exist :create))
  8.4058-    "Open FILE for input with given recognizes options, call THUNK with the resulting stream.
  8.4059-Other keys are accepted but discarded."
  8.4060-    (with-open-file (s pathname :direction :output
  8.4061-                                :element-type element-type
  8.4062-                                :external-format external-format
  8.4063-                                :if-exists if-exists
  8.4064-                                :if-does-not-exist if-does-not-exist)
  8.4065-      (funcall thunk s)))
  8.4066-
  8.4067-  (defmacro with-output-file ((var pathname &rest keys
  8.4068-                               &key element-type external-format if-exists if-does-not-exist)
  8.4069-                              &body body)
  8.4070-    (declare (ignore element-type external-format if-exists if-does-not-exist))
  8.4071-    `(call-with-output-file ,pathname #'(lambda (,var) ,@body) ,@keys))
  8.4072-
  8.4073-  (defun call-with-output (output function &key (element-type 'character))
  8.4074-    "Calls FUNCTION with an actual stream argument,
  8.4075-behaving like FORMAT with respect to how stream designators are interpreted:
  8.4076-If OUTPUT is a STREAM, use it as the stream.
  8.4077-If OUTPUT is NIL, use a STRING-OUTPUT-STREAM of given ELEMENT-TYPE as the stream, and
  8.4078-return the resulting string.
  8.4079-If OUTPUT is T, use *STANDARD-OUTPUT* as the stream.
  8.4080-If OUTPUT is a STRING with a fill-pointer, use it as a STRING-OUTPUT-STREAM of given ELEMENT-TYPE.
  8.4081-If OUTPUT is a PATHNAME, open the file and write to it, passing ELEMENT-TYPE to WITH-OUTPUT-FILE
  8.4082--- this latter as an extension since ASDF 3.1.
  8.4083-\(Proper ELEMENT-TYPE treatment since ASDF 3.3.4 only.\)
  8.4084-Otherwise, signal an error."
  8.4085-    (etypecase output
  8.4086-      (null
  8.4087-       (with-output-to-string (stream nil :element-type element-type) (funcall function stream)))
  8.4088-      ((eql t)
  8.4089-       (funcall function *standard-output*))
  8.4090-      (stream
  8.4091-       (funcall function output))
  8.4092-      (string
  8.4093-       (assert (fill-pointer output))
  8.4094-       (with-output-to-string (stream output :element-type element-type) (funcall function stream)))
  8.4095-      (pathname
  8.4096-       (call-with-output-file output function :element-type element-type)))))
  8.4097-
  8.4098-(with-upgradability ()
  8.4099-  (locally (declare #+sbcl (sb-ext:muffle-conditions style-warning))
  8.4100-    (handler-bind (#+sbcl (style-warning #'muffle-warning))
  8.4101-      (defmacro with-output ((output-var &optional (value output-var) &key element-type) &body body)
  8.4102-        "Bind OUTPUT-VAR to an output stream obtained from VALUE (default: previous binding
  8.4103-of OUTPUT-VAR) treated as a stream designator per CALL-WITH-OUTPUT. Evaluate BODY in
  8.4104-the scope of this binding."
  8.4105-        `(call-with-output ,value #'(lambda (,output-var) ,@body)
  8.4106-                           ,@(when element-type `(:element-type ,element-type)))))))
  8.4107-
  8.4108-(defun output-string (string &optional output)
  8.4109-  "If the desired OUTPUT is not NIL, print the string to the output; otherwise return the string"
  8.4110-  (if output
  8.4111-      (with-output (output) (princ string output))
  8.4112-      string))
  8.4113-
  8.4114-
  8.4115-;;; Input helpers
  8.4116-(with-upgradability ()
  8.4117-  (defun call-with-input-file (pathname thunk
  8.4118-                               &key
  8.4119-                                 (element-type *default-stream-element-type*)
  8.4120-                                 (external-format *utf-8-external-format*)
  8.4121-                                 (if-does-not-exist :error))
  8.4122-    "Open FILE for input with given recognizes options, call THUNK with the resulting stream.
  8.4123-Other keys are accepted but discarded."
  8.4124-    (with-open-file (s pathname :direction :input
  8.4125-                                :element-type element-type
  8.4126-                                :external-format external-format
  8.4127-                                :if-does-not-exist if-does-not-exist)
  8.4128-      (funcall thunk s)))
  8.4129-
  8.4130-  (defmacro with-input-file ((var pathname &rest keys
  8.4131-                              &key element-type external-format if-does-not-exist)
  8.4132-                             &body body)
  8.4133-    (declare (ignore element-type external-format if-does-not-exist))
  8.4134-    `(call-with-input-file ,pathname #'(lambda (,var) ,@body) ,@keys))
  8.4135-
  8.4136-  (defun call-with-input (input function &key keys)
  8.4137-    "Calls FUNCTION with an actual stream argument, interpreting
  8.4138-stream designators like READ, but also coercing strings to STRING-INPUT-STREAM,
  8.4139-and PATHNAME to FILE-STREAM.
  8.4140-If INPUT is a STREAM, use it as the stream.
  8.4141-If INPUT is NIL, use a *STANDARD-INPUT* as the stream.
  8.4142-If INPUT is T, use *TERMINAL-IO* as the stream.
  8.4143-If INPUT is a STRING, use it as a string-input-stream.
  8.4144-If INPUT is a PATHNAME, open it, passing KEYS to WITH-INPUT-FILE
  8.4145--- the latter is an extension since ASDF 3.1.
  8.4146-Otherwise, signal an error."
  8.4147-    (etypecase input
  8.4148-      (null (funcall function *standard-input*))
  8.4149-      ((eql t) (funcall function *terminal-io*))
  8.4150-      (stream (funcall function input))
  8.4151-      (string (with-input-from-string (stream input) (funcall function stream)))
  8.4152-      (pathname (apply 'call-with-input-file input function keys))))
  8.4153-
  8.4154-  (defmacro with-input ((input-var &optional (value input-var)) &body body)
  8.4155-    "Bind INPUT-VAR to an input stream, coercing VALUE (default: previous binding of INPUT-VAR)
  8.4156-as per CALL-WITH-INPUT, and evaluate BODY within the scope of this binding."
  8.4157-    `(call-with-input ,value #'(lambda (,input-var) ,@body)))
  8.4158-
  8.4159-  (defun input-string (&optional input)
  8.4160-    "If the desired INPUT is a string, return that string; otherwise slurp the INPUT into a string
  8.4161-and return that"
  8.4162-    (if (stringp input)
  8.4163-        input
  8.4164-        (with-input (input) (funcall 'slurp-stream-string input)))))
  8.4165-
  8.4166-;;; Null device
  8.4167-(with-upgradability ()
  8.4168-  (defun null-device-pathname ()
  8.4169-    "Pathname to a bit bucket device that discards any information written to it
  8.4170-and always returns EOF when read from"
  8.4171-    (os-cond
  8.4172-      ((os-unix-p) #p"/dev/null")
  8.4173-      ((os-windows-p) #p"NUL") ;; Q: how many Lisps accept the #p"NUL:" syntax?
  8.4174-      (t (error "No /dev/null on your OS"))))
  8.4175-  (defun call-with-null-input (fun &key element-type external-format if-does-not-exist)
  8.4176-    "Call FUN with an input stream that always returns end of file.
  8.4177-The keyword arguments are allowed for backward compatibility, but are ignored."
  8.4178-    (declare (ignore element-type external-format if-does-not-exist))
  8.4179-    (with-open-stream (input (make-concatenated-stream))
  8.4180-      (funcall fun input)))
  8.4181-  (defmacro with-null-input ((var &rest keys
  8.4182-                              &key element-type external-format if-does-not-exist)
  8.4183-                             &body body)
  8.4184-    (declare (ignore element-type external-format if-does-not-exist))
  8.4185-    "Evaluate BODY in a context when VAR is bound to an input stream that always returns end of file.
  8.4186-The keyword arguments are allowed for backward compatibility, but are ignored."
  8.4187-    `(call-with-null-input #'(lambda (,var) ,@body) ,@keys))
  8.4188-  (defun call-with-null-output (fun
  8.4189-                                &key (element-type *default-stream-element-type*)
  8.4190-                                  (external-format *utf-8-external-format*)
  8.4191-                                  (if-exists :overwrite)
  8.4192-                                  (if-does-not-exist :error))
  8.4193-    (declare (ignore element-type external-format if-exists if-does-not-exist))
  8.4194-    "Call FUN with an output stream that discards all output.
  8.4195-The keyword arguments are allowed for backward compatibility, but are ignored."
  8.4196-    (with-open-stream (output (make-broadcast-stream))
  8.4197-      (funcall fun output)))
  8.4198-  (defmacro with-null-output ((var &rest keys
  8.4199-                              &key element-type external-format if-does-not-exist if-exists)
  8.4200-                              &body body)
  8.4201-    "Evaluate BODY in a context when VAR is bound to an output stream that discards all output.
  8.4202-The keyword arguments are allowed for backward compatibility, but are ignored."
  8.4203-    (declare (ignore element-type external-format if-exists if-does-not-exist))
  8.4204-    `(call-with-null-output #'(lambda (,var) ,@body) ,@keys)))
  8.4205-
  8.4206-;;; Ensure output buffers are flushed
  8.4207-(with-upgradability ()
  8.4208-  (defun finish-outputs (&rest streams)
  8.4209-    "Finish output on the main output streams as well as any specified one.
  8.4210-Useful for portably flushing I/O before user input or program exit."
  8.4211-    ;; CCL notably buffers its stream output by default.
  8.4212-    (dolist (s (append streams
  8.4213-                       (list *stdout* *stderr* *error-output* *standard-output* *trace-output*
  8.4214-                             *debug-io* *terminal-io* *query-io*)))
  8.4215-      (ignore-errors (finish-output s)))
  8.4216-    (values))
  8.4217-
  8.4218-  (defun format! (stream format &rest args)
  8.4219-    "Just like format, but call finish-outputs before and after the output."
  8.4220-    (finish-outputs stream)
  8.4221-    (apply 'format stream format args)
  8.4222-    (finish-outputs stream))
  8.4223-
  8.4224-  (defun safe-format! (stream format &rest args)
  8.4225-    "Variant of FORMAT that is safe against both
  8.4226-dangerous syntax configuration and errors while printing."
  8.4227-    (with-safe-io-syntax ()
  8.4228-      (ignore-errors (apply 'format! stream format args))
  8.4229-      (finish-outputs stream)))) ; just in case format failed
  8.4230-
  8.4231-
  8.4232-;;; Simple Whole-Stream processing
  8.4233-(with-upgradability ()
  8.4234-  (defun copy-stream-to-stream (input output &key element-type buffer-size linewise prefix)
  8.4235-    "Copy the contents of the INPUT stream into the OUTPUT stream.
  8.4236-If LINEWISE is true, then read and copy the stream line by line, with an optional PREFIX.
  8.4237-Otherwise, using WRITE-SEQUENCE using a buffer of size BUFFER-SIZE."
  8.4238-    (with-open-stream (input input)
  8.4239-      (if linewise
  8.4240-          (loop :for (line eof) = (multiple-value-list (read-line input nil nil))
  8.4241-                :while line :do
  8.4242-                  (when prefix (princ prefix output))
  8.4243-                  (princ line output)
  8.4244-                  (unless eof (terpri output))
  8.4245-                  (finish-output output)
  8.4246-                  (when eof (return)))
  8.4247-          (loop
  8.4248-            :with buffer-size = (or buffer-size 8192)
  8.4249-            :with buffer = (make-array (list buffer-size) :element-type (or element-type 'character))
  8.4250-            :for end = (read-sequence buffer input)
  8.4251-            :until (zerop end)
  8.4252-            :do (write-sequence buffer output :end end)
  8.4253-                (when (< end buffer-size) (return))))))
  8.4254-
  8.4255-  (defun concatenate-files (inputs output)
  8.4256-    "create a new OUTPUT file the contents of which a the concatenate of the INPUTS files."
  8.4257-    (with-open-file (o output :element-type '(unsigned-byte 8)
  8.4258-                              :direction :output :if-exists :rename-and-delete)
  8.4259-      (dolist (input inputs)
  8.4260-        (with-open-file (i input :element-type '(unsigned-byte 8)
  8.4261-                                 :direction :input :if-does-not-exist :error)
  8.4262-          (copy-stream-to-stream i o :element-type '(unsigned-byte 8))))))
  8.4263-
  8.4264-  (defun copy-file (input output)
  8.4265-    "Copy contents of the INPUT file to the OUTPUT file"
  8.4266-    ;; Not available on LW personal edition or LW 6.0 on Mac: (lispworks:copy-file i f)
  8.4267-    #+allegro
  8.4268-    (excl.osi:copy-file input output)
  8.4269-    #+ecl
  8.4270-    (ext:copy-file input output)
  8.4271-    #-(or allegro ecl)
  8.4272-    (concatenate-files (list input) output))
  8.4273-
  8.4274-  (defun slurp-stream-string (input &key (element-type 'character) stripped)
  8.4275-    "Read the contents of the INPUT stream as a string"
  8.4276-    (let ((string
  8.4277-            (with-open-stream (input input)
  8.4278-              (with-output-to-string (output nil :element-type element-type)
  8.4279-                (copy-stream-to-stream input output :element-type element-type)))))
  8.4280-      (if stripped (stripln string) string)))
  8.4281-
  8.4282-  (defun slurp-stream-lines (input &key count)
  8.4283-    "Read the contents of the INPUT stream as a list of lines, return those lines.
  8.4284-
  8.4285-Note: relies on the Lisp's READ-LINE, but additionally removes any remaining CR
  8.4286-from the line-ending if the file or stream had CR+LF but Lisp only removed LF.
  8.4287-
  8.4288-Read no more than COUNT lines."
  8.4289-    (check-type count (or null integer))
  8.4290-    (with-open-stream (input input)
  8.4291-      (loop :for n :from 0
  8.4292-            :for l = (and (or (not count) (< n count))
  8.4293-                          (read-line input nil nil))
  8.4294-            ;; stripln: to remove CR when the OS sends CRLF and Lisp only remove LF
  8.4295-            :while l :collect (stripln l))))
  8.4296-
  8.4297-  (defun slurp-stream-line (input &key (at 0))
  8.4298-    "Read the contents of the INPUT stream as a list of lines,
  8.4299-then return the ACCESS-AT of that list of lines using the AT specifier.
  8.4300-PATH defaults to 0, i.e. return the first line.
  8.4301-PATH is typically an integer, or a list of an integer and a function.
  8.4302-If PATH is NIL, it will return all the lines in the file.
  8.4303-
  8.4304-The stream will not be read beyond the Nth lines,
  8.4305-where N is the index specified by path
  8.4306-if path is either an integer or a list that starts with an integer."
  8.4307-    (access-at (slurp-stream-lines input :count (access-at-count at)) at))
  8.4308-
  8.4309-  (defun slurp-stream-forms (input &key count)
  8.4310-    "Read the contents of the INPUT stream as a list of forms,
  8.4311-and return those forms.
  8.4312-
  8.4313-If COUNT is null, read to the end of the stream;
  8.4314-if COUNT is an integer, stop after COUNT forms were read.
  8.4315-
  8.4316-BEWARE: be sure to use WITH-SAFE-IO-SYNTAX, or some variant thereof"
  8.4317-    (check-type count (or null integer))
  8.4318-    (loop :with eof = '#:eof
  8.4319-          :for n :from 0
  8.4320-          :for form = (if (and count (>= n count))
  8.4321-                          eof
  8.4322-                          (read-preserving-whitespace input nil eof))
  8.4323-          :until (eq form eof) :collect form))
  8.4324-
  8.4325-  (defun slurp-stream-form (input &key (at 0))
  8.4326-    "Read the contents of the INPUT stream as a list of forms,
  8.4327-then return the ACCESS-AT of these forms following the AT.
  8.4328-AT defaults to 0, i.e. return the first form.
  8.4329-AT is typically a list of integers.
  8.4330-If AT is NIL, it will return all the forms in the file.
  8.4331-
  8.4332-The stream will not be read beyond the Nth form,
  8.4333-where N is the index specified by path,
  8.4334-if path is either an integer or a list that starts with an integer.
  8.4335-
  8.4336-BEWARE: be sure to use WITH-SAFE-IO-SYNTAX, or some variant thereof"
  8.4337-    (access-at (slurp-stream-forms input :count (access-at-count at)) at))
  8.4338-
  8.4339-  (defun read-file-string (file &rest keys)
  8.4340-    "Open FILE with option KEYS, read its contents as a string"
  8.4341-    (apply 'call-with-input-file file 'slurp-stream-string keys))
  8.4342-
  8.4343-  (defun read-file-lines (file &rest keys)
  8.4344-    "Open FILE with option KEYS, read its contents as a list of lines
  8.4345-BEWARE: be sure to use WITH-SAFE-IO-SYNTAX, or some variant thereof"
  8.4346-    (apply 'call-with-input-file file 'slurp-stream-lines keys))
  8.4347-
  8.4348-  (defun read-file-line (file &rest keys &key (at 0) &allow-other-keys)
  8.4349-    "Open input FILE with option KEYS (except AT),
  8.4350-and read its contents as per SLURP-STREAM-LINE with given AT specifier.
  8.4351-BEWARE: be sure to use WITH-SAFE-IO-SYNTAX, or some variant thereof"
  8.4352-    (apply 'call-with-input-file file
  8.4353-           #'(lambda (input) (slurp-stream-line input :at at))
  8.4354-           (remove-plist-key :at keys)))
  8.4355-
  8.4356-  (defun read-file-forms (file &rest keys &key count &allow-other-keys)
  8.4357-    "Open input FILE with option KEYS (except COUNT),
  8.4358-and read its contents as per SLURP-STREAM-FORMS with given COUNT.
  8.4359-If COUNT is null, read to the end of the stream;
  8.4360-if COUNT is an integer, stop after COUNT forms were read.
  8.4361-BEWARE: be sure to use WITH-SAFE-IO-SYNTAX, or some variant thereof"
  8.4362-    (apply 'call-with-input-file file
  8.4363-           #'(lambda (input) (slurp-stream-forms input :count count))
  8.4364-           (remove-plist-key :count keys)))
  8.4365-
  8.4366-  (defun read-file-form (file &rest keys &key (at 0) &allow-other-keys)
  8.4367-    "Open input FILE with option KEYS (except AT),
  8.4368-and read its contents as per SLURP-STREAM-FORM with given AT specifier.
  8.4369-BEWARE: be sure to use WITH-SAFE-IO-SYNTAX, or some variant thereof"
  8.4370-    (apply 'call-with-input-file file
  8.4371-           #'(lambda (input) (slurp-stream-form input :at at))
  8.4372-           (remove-plist-key :at keys)))
  8.4373-
  8.4374-  (defun safe-read-file-line (pathname &rest keys &key (package :cl) &allow-other-keys)
  8.4375-    "Reads the specified line from the top of a file using a safe standardized syntax.
  8.4376-Extracts the line using READ-FILE-LINE,
  8.4377-within an WITH-SAFE-IO-SYNTAX using the specified PACKAGE."
  8.4378-    (with-safe-io-syntax (:package package)
  8.4379-      (apply 'read-file-line pathname (remove-plist-key :package keys))))
  8.4380-
  8.4381-  (defun safe-read-file-form (pathname &rest keys &key (package :cl) &allow-other-keys)
  8.4382-    "Reads the specified form from the top of a file using a safe standardized syntax.
  8.4383-Extracts the form using READ-FILE-FORM,
  8.4384-within an WITH-SAFE-IO-SYNTAX using the specified PACKAGE."
  8.4385-    (with-safe-io-syntax (:package package)
  8.4386-      (apply 'read-file-form pathname (remove-plist-key :package keys))))
  8.4387-
  8.4388-  (defun eval-input (input)
  8.4389-    "Portably read and evaluate forms from INPUT, return the last values."
  8.4390-    (with-input (input)
  8.4391-      (loop :with results :with eof ='#:eof
  8.4392-            :for form = (read input nil eof)
  8.4393-            :until (eq form eof)
  8.4394-            :do (setf results (multiple-value-list (eval form)))
  8.4395-            :finally (return (values-list results)))))
  8.4396-
  8.4397-  (defun eval-thunk (thunk)
  8.4398-    "Evaluate a THUNK of code:
  8.4399-If a function, FUNCALL it without arguments.
  8.4400-If a constant literal and not a sequence, return it.
  8.4401-If a cons or a symbol, EVAL it.
  8.4402-If a string, repeatedly read and evaluate from it, returning the last values."
  8.4403-    (etypecase thunk
  8.4404-      ((or boolean keyword number character pathname) thunk)
  8.4405-      ((or cons symbol) (eval thunk))
  8.4406-      (function (funcall thunk))
  8.4407-      (string (eval-input thunk))))
  8.4408-
  8.4409-  (defun standard-eval-thunk (thunk &key (package :cl))
  8.4410-    "Like EVAL-THUNK, but in a more standardized evaluation context."
  8.4411-    ;; Note: it's "standard-" not "safe-", because evaluation is never safe.
  8.4412-    (when thunk
  8.4413-      (with-safe-io-syntax (:package package)
  8.4414-        (let ((*read-eval* t))
  8.4415-          (eval-thunk thunk))))))
  8.4416-
  8.4417-(with-upgradability ()
  8.4418-  (defun println (x &optional (stream *standard-output*))
  8.4419-    "Variant of PRINC that also calls TERPRI afterwards"
  8.4420-    (princ x stream) (terpri stream) (finish-output stream) (values))
  8.4421-
  8.4422-  (defun writeln (x &rest keys &key (stream *standard-output*) &allow-other-keys)
  8.4423-    "Variant of WRITE that also calls TERPRI afterwards"
  8.4424-    (apply 'write x keys) (terpri stream) (finish-output stream) (values)))
  8.4425-
  8.4426-
  8.4427-;;; Using temporary files
  8.4428-(with-upgradability ()
  8.4429-  (defun default-temporary-directory ()
  8.4430-    "Return a default directory to use for temporary files"
  8.4431-    (os-cond
  8.4432-      ((os-unix-p)
  8.4433-       (or (getenv-pathname "TMPDIR" :ensure-directory t)
  8.4434-           (parse-native-namestring "/tmp/")))
  8.4435-      ((os-windows-p)
  8.4436-       (getenv-pathname "TEMP" :ensure-directory t))
  8.4437-      (t (subpathname (user-homedir-pathname) "tmp/"))))
  8.4438-
  8.4439-  (defvar *temporary-directory* nil "User-configurable location for temporary files")
  8.4440-
  8.4441-  (defun temporary-directory ()
  8.4442-    "Return a directory to use for temporary files"
  8.4443-    (or *temporary-directory* (default-temporary-directory)))
  8.4444-
  8.4445-  (defun setup-temporary-directory ()
  8.4446-    "Configure a default temporary directory to use."
  8.4447-    (setf *temporary-directory* (default-temporary-directory))
  8.4448-    #+gcl (setf system::*tmp-dir* *temporary-directory*))
  8.4449-
  8.4450-  (defun call-with-temporary-file
  8.4451-      (thunk &key
  8.4452-               (want-stream-p t) (want-pathname-p t) (direction :io) keep after
  8.4453-               directory (type "tmp" typep) prefix (suffix (when typep "-tmp"))
  8.4454-               (element-type *default-stream-element-type*)
  8.4455-               (external-format *utf-8-external-format*))
  8.4456-    "Call a THUNK with stream and/or pathname arguments identifying a temporary file.
  8.4457-
  8.4458-The temporary file's pathname will be based on concatenating
  8.4459-PREFIX (or \"tmp\" if it's NIL), a random alphanumeric string,
  8.4460-and optional SUFFIX (defaults to \"-tmp\" if a type was provided)
  8.4461-and TYPE (defaults to \"tmp\", using a dot as separator if not NIL),
  8.4462-within DIRECTORY (defaulting to the TEMPORARY-DIRECTORY) if the PREFIX isn't absolute.
  8.4463-
  8.4464-The file will be open with specified DIRECTION (defaults to :IO),
  8.4465-ELEMENT-TYPE (defaults to *DEFAULT-STREAM-ELEMENT-TYPE*) and
  8.4466-EXTERNAL-FORMAT (defaults to *UTF-8-EXTERNAL-FORMAT*).
  8.4467-If WANT-STREAM-P is true (the defaults to T), then THUNK will then be CALL-FUNCTION'ed
  8.4468-with the stream and the pathname (if WANT-PATHNAME-P is true, defaults to T),
  8.4469-and stream will be closed after the THUNK exits (either normally or abnormally).
  8.4470-If WANT-STREAM-P is false, then WANT-PATHAME-P must be true, and then
  8.4471-THUNK is only CALL-FUNCTION'ed after the stream is closed, with the pathname as argument.
  8.4472-Upon exit of THUNK, the AFTER thunk if defined is CALL-FUNCTION'ed with the pathname as argument.
  8.4473-If AFTER is defined, its results are returned, otherwise, the results of THUNK are returned.
  8.4474-Finally, the file will be deleted, unless the KEEP argument when CALL-FUNCTION'ed returns true."
  8.4475-    #+xcl (declare (ignorable typep))
  8.4476-    (check-type direction (member :output :io))
  8.4477-    (assert (or want-stream-p want-pathname-p))
  8.4478-    (loop
  8.4479-      :with prefix-pn = (ensure-absolute-pathname
  8.4480-                         (or prefix "tmp")
  8.4481-                         (or (ensure-pathname
  8.4482-                              directory
  8.4483-                              :namestring :native
  8.4484-                              :ensure-directory t
  8.4485-                              :ensure-physical t)
  8.4486-                             #'temporary-directory))
  8.4487-      :with prefix-nns = (native-namestring prefix-pn)
  8.4488-      :with results = (progn (ensure-directories-exist prefix-pn)
  8.4489-                             ())
  8.4490-      :for counter :from (random (expt 36 #-gcl 8 #+gcl 5))
  8.4491-      :for pathname = (parse-native-namestring
  8.4492-                       (format nil "~A~36R~@[~A~]~@[.~A~]"
  8.4493-                               prefix-nns counter suffix (unless (eq type :unspecific) type)))
  8.4494-      :for okp = nil :do
  8.4495-        ;; TODO: on Unix, do something about umask
  8.4496-        ;; TODO: on Unix, audit the code so we make sure it uses O_CREAT|O_EXCL
  8.4497-        ;; TODO: on Unix, use CFFI and mkstemp --
  8.4498-        ;; except UIOP is precisely meant to not depend on CFFI or on anything! Grrrr.
  8.4499-        ;; Can we at least design some hook?
  8.4500-        (unwind-protect
  8.4501-             (progn
  8.4502-               (ensure-directories-exist pathname)
  8.4503-               (with-open-file (stream pathname
  8.4504-                                       :direction direction
  8.4505-                                       :element-type element-type
  8.4506-                                       :external-format external-format
  8.4507-                                       :if-exists nil :if-does-not-exist :create)
  8.4508-                 (when stream
  8.4509-                   (setf okp pathname)
  8.4510-                   (when want-stream-p
  8.4511-                     ;; Note: can't return directly from within with-open-file
  8.4512-                     ;; or the non-local return causes the file creation to be undone.
  8.4513-                     (setf results (multiple-value-list
  8.4514-                                    (if want-pathname-p
  8.4515-                                        (call-function thunk stream pathname)
  8.4516-                                        (call-function thunk stream)))))))
  8.4517-               ;; if we don't want a stream, then we must call the thunk *after*
  8.4518-               ;; the stream is closed, but only if it was successfully opened.
  8.4519-               (when okp
  8.4520-                 (when (and want-pathname-p (not want-stream-p))
  8.4521-                   (setf results (multiple-value-list (call-function thunk okp))))
  8.4522-                 ;; if the stream was successfully opened, then return a value,
  8.4523-                 ;; either one computed already, or one from AFTER, if that exists.
  8.4524-                 (if after
  8.4525-                     (return (call-function after pathname))
  8.4526-                     (return (values-list results)))))
  8.4527-          (when (and okp (not (call-function keep)))
  8.4528-            (ignore-errors (delete-file-if-exists okp))))))
  8.4529-
  8.4530-  (defmacro with-temporary-file ((&key (stream (gensym "STREAM") streamp)
  8.4531-                                    (pathname (gensym "PATHNAME") pathnamep)
  8.4532-                                    directory prefix suffix type
  8.4533-                                    keep direction element-type external-format)
  8.4534-                                 &body body)
  8.4535-    "Evaluate BODY where the symbols specified by keyword arguments
  8.4536-STREAM and PATHNAME (if respectively specified) are bound corresponding
  8.4537-to a newly created temporary file ready for I/O, as per CALL-WITH-TEMPORARY-FILE.
  8.4538-At least one of STREAM or PATHNAME must be specified.
  8.4539-If the STREAM is not specified, it will be closed before the BODY is evaluated.
  8.4540-If STREAM is specified, then the :CLOSE-STREAM label if it appears in the BODY,
  8.4541-separates forms run before and after the stream is closed.
  8.4542-The values of the last form of the BODY (not counting the separating :CLOSE-STREAM) are returned.
  8.4543-Upon success, the KEEP form is evaluated and the file is is deleted unless it evaluates to TRUE."
  8.4544-    (check-type stream symbol)
  8.4545-    (check-type pathname symbol)
  8.4546-    (assert (or streamp pathnamep))
  8.4547-    (let* ((afterp (position :close-stream body))
  8.4548-           (before (if afterp (subseq body 0 afterp) body))
  8.4549-           (after (when afterp (subseq body (1+ afterp))))
  8.4550-           (beforef (gensym "BEFORE"))
  8.4551-           (afterf (gensym "AFTER")))
  8.4552-      (when (eql afterp 0)
  8.4553-        (style-warn ":CLOSE-STREAM should not be the first form of BODY in WITH-TEMPORARY-FILE. Instead, do not provide a STREAM."))
  8.4554-      `(flet (,@(when before
  8.4555-                  `((,beforef (,@(when streamp `(,stream)) ,@(when pathnamep `(,pathname)))
  8.4556-                       ,@(when after `((declare (ignorable ,pathname))))
  8.4557-                       ,@before)))
  8.4558-              ,@(when after
  8.4559-                  (assert pathnamep)
  8.4560-                  `((,afterf (,pathname) ,@after))))
  8.4561-         #-gcl (declare (dynamic-extent ,@(when before `(#',beforef)) ,@(when after `(#',afterf))))
  8.4562-         (call-with-temporary-file
  8.4563-          ,(when before `#',beforef)
  8.4564-          :want-stream-p ,streamp
  8.4565-          :want-pathname-p ,pathnamep
  8.4566-          ,@(when direction `(:direction ,direction))
  8.4567-          ,@(when directory `(:directory ,directory))
  8.4568-          ,@(when prefix `(:prefix ,prefix))
  8.4569-          ,@(when suffix `(:suffix ,suffix))
  8.4570-          ,@(when type `(:type ,type))
  8.4571-          ,@(when keep `(:keep ,keep))
  8.4572-          ,@(when after `(:after #',afterf))
  8.4573-          ,@(when element-type `(:element-type ,element-type))
  8.4574-          ,@(when external-format `(:external-format ,external-format))))))
  8.4575-
  8.4576-  (defun get-temporary-file (&key directory prefix suffix type (keep t))
  8.4577-    (with-temporary-file (:pathname pn :keep keep
  8.4578-                          :directory directory :prefix prefix :suffix suffix :type type)
  8.4579-      pn))
  8.4580-
  8.4581-  ;; Temporary pathnames in simple cases where no contention is assumed
  8.4582-  (defun add-pathname-suffix (pathname suffix &rest keys)
  8.4583-    "Add a SUFFIX to the name of a PATHNAME, return a new pathname.
  8.4584-Further KEYS can be passed to MAKE-PATHNAME."
  8.4585-    (apply 'make-pathname :name (strcat (pathname-name pathname) suffix)
  8.4586-                          :defaults pathname keys))
  8.4587-
  8.4588-  (defun tmpize-pathname (x)
  8.4589-    "Return a new pathname modified from X by adding a trivial random suffix.
  8.4590-A new empty file with said temporary pathname is created, to ensure there is no
  8.4591-clash with any concurrent process attempting the same thing."
  8.4592-    (let* ((px (ensure-pathname x :ensure-physical t))
  8.4593-           (prefix (if-let (n (pathname-name px)) (strcat n "-tmp") "tmp"))
  8.4594-           (directory (pathname-directory-pathname px)))
  8.4595-      ;; Genera uses versioned pathnames -- If we leave the empty file in place,
  8.4596-      ;; the system will create a new version of the file when the caller opens
  8.4597-      ;; it for output. That empty file will remain after the operation is completed.
  8.4598-      ;; As Genera is a single core processor, the possibility of a name conflict is
  8.4599-      ;; minimal if not nil. (And, in the event of a collision, the two processes
  8.4600-      ;; would be writing to different versions of the file.)
  8.4601-      (get-temporary-file :directory directory :prefix prefix :type (pathname-type px)
  8.4602-                          #+genera :keep #+genera nil)))
  8.4603-
  8.4604-  (defun call-with-staging-pathname (pathname fun)
  8.4605-    "Calls FUN with a staging pathname, and atomically
  8.4606-renames the staging pathname to the PATHNAME in the end.
  8.4607-NB: this protects only against failure of the program, not against concurrent attempts.
  8.4608-For the latter case, we ought pick a random suffix and atomically open it."
  8.4609-    (let* ((pathname (pathname pathname))
  8.4610-           (staging (tmpize-pathname pathname)))
  8.4611-      (unwind-protect
  8.4612-           (multiple-value-prog1
  8.4613-               (funcall fun staging)
  8.4614-             (rename-file-overwriting-target staging pathname))
  8.4615-        (delete-file-if-exists staging))))
  8.4616-
  8.4617-  (defmacro with-staging-pathname ((pathname-var &optional (pathname-value pathname-var)) &body body)
  8.4618-    "Trivial syntax wrapper for CALL-WITH-STAGING-PATHNAME"
  8.4619-    `(call-with-staging-pathname ,pathname-value #'(lambda (,pathname-var) ,@body))))
  8.4620-
  8.4621-(with-upgradability ()
  8.4622-  (defun file-stream-p (stream)
  8.4623-    (typep stream 'file-stream))
  8.4624-  (defun file-or-synonym-stream-p (stream)
  8.4625-    (or (file-stream-p stream)
  8.4626-        (and (typep stream 'synonym-stream)
  8.4627-             (file-or-synonym-stream-p
  8.4628-              (symbol-value (synonym-stream-symbol stream)))))))
  8.4629-;;;; -------------------------------------------------------------------------
  8.4630-;;;; Starting, Stopping, Dumping a Lisp image
  8.4631-
  8.4632-(uiop/package:define-package :uiop/image
  8.4633-  (:use :uiop/common-lisp :uiop/package :uiop/utility :uiop/pathname :uiop/stream :uiop/os)
  8.4634-  (:export
  8.4635-   #:*image-dumped-p* #:raw-command-line-arguments #:*command-line-arguments*
  8.4636-   #:command-line-arguments #:raw-command-line-arguments #:setup-command-line-arguments #:argv0
  8.4637-   #:*lisp-interaction*
  8.4638-   #:fatal-condition #:fatal-condition-p
  8.4639-   #:handle-fatal-condition
  8.4640-   #:call-with-fatal-condition-handler #:with-fatal-condition-handler
  8.4641-   #:*image-restore-hook* #:*image-prelude* #:*image-entry-point*
  8.4642-   #:*image-postlude* #:*image-dump-hook*
  8.4643-   #:quit #:die #:raw-print-backtrace #:print-backtrace #:print-condition-backtrace
  8.4644-   #:shell-boolean-exit
  8.4645-   #:register-image-restore-hook #:register-image-dump-hook
  8.4646-   #:call-image-restore-hook #:call-image-dump-hook
  8.4647-   #:restore-image #:dump-image #:create-image
  8.4648-))
  8.4649-(in-package :uiop/image)
  8.4650-
  8.4651-(with-upgradability ()
  8.4652-  (defvar *lisp-interaction* t
  8.4653-    "Is this an interactive Lisp environment, or is it batch processing?")
  8.4654-
  8.4655-  (defvar *command-line-arguments* nil
  8.4656-    "Command-line arguments")
  8.4657-
  8.4658-  (defvar *image-dumped-p* nil ; may matter as to how to get to command-line-arguments
  8.4659-    "Is this a dumped image? As a standalone executable?")
  8.4660-
  8.4661-  (defvar *image-restore-hook* nil
  8.4662-    "Functions to call (in reverse order) when the image is restored")
  8.4663-
  8.4664-  (defvar *image-restored-p* nil
  8.4665-    "Has the image been restored? A boolean, or :in-progress while restoring, :in-regress while dumping")
  8.4666-
  8.4667-  (defvar *image-prelude* nil
  8.4668-    "a form to evaluate, or string containing forms to read and evaluate
  8.4669-when the image is restarted, but before the entry point is called.")
  8.4670-
  8.4671-  (defvar *image-entry-point* nil
  8.4672-    "a function with which to restart the dumped image when execution is restored from it.")
  8.4673-
  8.4674-  (defvar *image-postlude* nil
  8.4675-    "a form to evaluate, or string containing forms to read and evaluate
  8.4676-before the image dump hooks are called and before the image is dumped.")
  8.4677-
  8.4678-  (defvar *image-dump-hook* nil
  8.4679-    "Functions to call (in order) when before an image is dumped"))
  8.4680-
  8.4681-(eval-when (#-lispworks :compile-toplevel :load-toplevel :execute)
  8.4682-  (deftype fatal-condition ()
  8.4683-    `(and serious-condition #+clozure (not ccl:process-reset))))
  8.4684-
  8.4685-;;; Exiting properly or im-
  8.4686-(with-upgradability ()
  8.4687-  (defun quit (&optional (code 0) (finish-output t))
  8.4688-    "Quits from the Lisp world, with the given exit status if provided.
  8.4689-This is designed to abstract away the implementation specific quit forms."
  8.4690-    (when finish-output ;; essential, for ClozureCL, and for standard compliance.
  8.4691-      (finish-outputs))
  8.4692-    #+(or abcl xcl) (ext:quit :status code)
  8.4693-    #+allegro (excl:exit code :quiet t)
  8.4694-    #+(or clasp ecl) (si:quit code)
  8.4695-    #+clisp (ext:quit code)
  8.4696-    #+clozure (ccl:quit code)
  8.4697-    #+cormanlisp (win32:exitprocess code)
  8.4698-    #+(or cmucl scl) (unix:unix-exit code)
  8.4699-    #+gcl (system:quit code)
  8.4700-    #+genera (error "~S: You probably don't want to Halt Genera. (code: ~S)" 'quit code)
  8.4701-    #+lispworks (lispworks:quit :status code :confirm nil :return nil :ignore-errors-p t)
  8.4702-    #+mcl (progn code (ccl:quit)) ;; or should we use FFI to call libc's exit(3) ?
  8.4703-    #+mkcl (mk-ext:quit :exit-code code)
  8.4704-    #+sbcl #.(let ((exit (find-symbol* :exit :sb-ext nil))
  8.4705-                   (quit (find-symbol* :quit :sb-ext nil)))
  8.4706-               (cond
  8.4707-                 (exit `(,exit :code code :abort (not finish-output)))
  8.4708-                 (quit `(,quit :unix-status code :recklessly-p (not finish-output)))))
  8.4709-    #-(or abcl allegro clasp clisp clozure cmucl ecl gcl genera lispworks mcl mkcl sbcl scl xcl)
  8.4710-    (not-implemented-error 'quit "(called with exit code ~S)" code))
  8.4711-
  8.4712-  (defun die (code format &rest arguments)
  8.4713-    "Die in error with some error message"
  8.4714-    (with-safe-io-syntax ()
  8.4715-      (ignore-errors
  8.4716-       (format! *stderr* "~&~?~&" format arguments)))
  8.4717-    (quit code))
  8.4718-
  8.4719-  (defun raw-print-backtrace (&key (stream *debug-io*) count condition)
  8.4720-    "Print a backtrace, directly accessing the implementation"
  8.4721-    (declare (ignorable stream count condition))
  8.4722-    #+abcl
  8.4723-    (loop :for i :from 0
  8.4724-          :for frame :in (sys:backtrace (or count most-positive-fixnum)) :do
  8.4725-            (safe-format! stream "~&~D: ~A~%" i frame))
  8.4726-    #+allegro
  8.4727-    (let ((*terminal-io* stream)
  8.4728-          (*standard-output* stream)
  8.4729-          (tpl:*zoom-print-circle* *print-circle*)
  8.4730-          (tpl:*zoom-print-level* *print-level*)
  8.4731-          (tpl:*zoom-print-length* *print-length*))
  8.4732-      (tpl:do-command "zoom"
  8.4733-        :from-read-eval-print-loop nil
  8.4734-        :count (or count t)
  8.4735-        :all t))
  8.4736-    #+clasp
  8.4737-    (clasp-debug:print-backtrace :stream stream :count count)
  8.4738-    #+(or ecl mkcl)
  8.4739-    (let* ((top (si:ihs-top))
  8.4740-           (repeats (if count (min top count) top))
  8.4741-           (backtrace (loop :for ihs :from 0 :below top
  8.4742-                            :collect (list (si::ihs-fun ihs)
  8.4743-                                           (si::ihs-env ihs)))))
  8.4744-      (loop :for i :from 0 :below repeats
  8.4745-            :for frame :in (nreverse backtrace) :do
  8.4746-              (safe-format! stream "~&~D: ~S~%" i frame)))
  8.4747-    #+clisp
  8.4748-    (system::print-backtrace :out stream :limit count)
  8.4749-    #+(or clozure mcl)
  8.4750-    (let ((*debug-io* stream))
  8.4751-      #+clozure (ccl:print-call-history :count count :start-frame-number 1)
  8.4752-      #+mcl (ccl:print-call-history :detailed-p nil)
  8.4753-      (finish-output stream))
  8.4754-    #+(or cmucl scl)
  8.4755-    (let ((debug:*debug-print-level* *print-level*)
  8.4756-          (debug:*debug-print-length* *print-length*))
  8.4757-      (debug:backtrace (or count most-positive-fixnum) stream))
  8.4758-    #+gcl
  8.4759-    (let ((*debug-io* stream))
  8.4760-      (ignore-errors
  8.4761-       (with-safe-io-syntax ()
  8.4762-         (if condition
  8.4763-             (conditions::condition-backtrace condition)
  8.4764-             (system::simple-backtrace)))))
  8.4765-    #+lispworks
  8.4766-    (let ((dbg::*debugger-stack*
  8.4767-            (dbg::grab-stack nil :how-many (or count most-positive-fixnum)))
  8.4768-          (*debug-io* stream)
  8.4769-          (dbg:*debug-print-level* *print-level*)
  8.4770-          (dbg:*debug-print-length* *print-length*))
  8.4771-      (dbg:bug-backtrace nil))
  8.4772-    #+mezzano
  8.4773-    (let ((*standard-output* stream))
  8.4774-      (sys.int::backtrace count))
  8.4775-    #+sbcl
  8.4776-    (sb-debug:print-backtrace :stream stream :count (or count most-positive-fixnum))
  8.4777-    #+xcl
  8.4778-    (loop :for i :from 0 :below (or count most-positive-fixnum)
  8.4779-          :for frame :in (extensions:backtrace-as-list) :do
  8.4780-            (safe-format! stream "~&~D: ~S~%" i frame)))
  8.4781-
  8.4782-  (defun print-backtrace (&rest keys &key stream count condition)
  8.4783-    "Print a backtrace"
  8.4784-    (declare (ignore stream count condition))
  8.4785-    (with-safe-io-syntax (:package :cl)
  8.4786-      (let ((*print-readably* nil)
  8.4787-            (*print-circle* t)
  8.4788-            (*print-miser-width* 75)
  8.4789-            (*print-length* nil)
  8.4790-            (*print-level* nil)
  8.4791-            (*print-pretty* t))
  8.4792-        (ignore-errors (apply 'raw-print-backtrace keys)))))
  8.4793-
  8.4794-  (defun print-condition-backtrace (condition &key (stream *stderr*) count)
  8.4795-    "Print a condition after a backtrace triggered by that condition"
  8.4796-    ;; We print the condition *after* the backtrace,
  8.4797-    ;; for the sake of who sees the backtrace at a terminal.
  8.4798-    ;; It is up to the caller to print the condition *before*, with some context.
  8.4799-    (print-backtrace :stream stream :count count :condition condition)
  8.4800-    (when condition
  8.4801-      (safe-format! stream "~&Above backtrace due to this condition:~%~A~&"
  8.4802-                    condition)))
  8.4803-
  8.4804-  (defun fatal-condition-p (condition)
  8.4805-    "Is the CONDITION fatal?"
  8.4806-    (typep condition 'fatal-condition))
  8.4807-
  8.4808-  (defun handle-fatal-condition (condition)
  8.4809-    "Handle a fatal CONDITION:
  8.4810-depending on whether *LISP-INTERACTION* is set, enter debugger or die"
  8.4811-    (cond
  8.4812-      (*lisp-interaction*
  8.4813-       (invoke-debugger condition))
  8.4814-      (t
  8.4815-       (safe-format! *stderr* "~&Fatal condition:~%~A~%" condition)
  8.4816-       (print-condition-backtrace condition :stream *stderr*)
  8.4817-       (die 99 "~A" condition))))
  8.4818-
  8.4819-  (defun call-with-fatal-condition-handler (thunk)
  8.4820-    "Call THUNK in a context where fatal conditions are appropriately handled"
  8.4821-    (handler-bind ((fatal-condition #'handle-fatal-condition))
  8.4822-      (funcall thunk)))
  8.4823-
  8.4824-  (defmacro with-fatal-condition-handler ((&optional) &body body)
  8.4825-    "Execute BODY in a context where fatal conditions are appropriately handled"
  8.4826-    `(call-with-fatal-condition-handler #'(lambda () ,@body)))
  8.4827-
  8.4828-  (defun shell-boolean-exit (x)
  8.4829-    "Quit with a return code that is 0 iff argument X is true"
  8.4830-    (quit (if x 0 1))))
  8.4831-
  8.4832-
  8.4833-;;; Using image hooks
  8.4834-(with-upgradability ()
  8.4835-  (defun register-image-restore-hook (hook &optional (call-now-p t))
  8.4836-    "Regiter a hook function to be run when restoring a dumped image"
  8.4837-    (register-hook-function '*image-restore-hook* hook call-now-p))
  8.4838-
  8.4839-  (defun register-image-dump-hook (hook &optional (call-now-p nil))
  8.4840-    "Register a the hook function to be run before to dump an image"
  8.4841-    (register-hook-function '*image-dump-hook* hook call-now-p))
  8.4842-
  8.4843-  (defun call-image-restore-hook ()
  8.4844-    "Call the hook functions registered to be run when restoring a dumped image"
  8.4845-    (call-functions (reverse *image-restore-hook*)))
  8.4846-
  8.4847-  (defun call-image-dump-hook ()
  8.4848-    "Call the hook functions registered to be run before to dump an image"
  8.4849-    (call-functions *image-dump-hook*)))
  8.4850-
  8.4851-
  8.4852-;;; Proper command-line arguments
  8.4853-(with-upgradability ()
  8.4854-  (defun raw-command-line-arguments ()
  8.4855-    "Find what the actual command line for this process was."
  8.4856-    #+abcl ext:*command-line-argument-list* ; Use 1.0.0 or later!
  8.4857-    #+allegro (sys:command-line-arguments) ; default: :application t
  8.4858-    #+(or clasp ecl) (loop :for i :from 0 :below (si:argc) :collect (si:argv i))
  8.4859-    #+clisp (coerce (ext:argv) 'list)
  8.4860-    #+clozure ccl:*command-line-argument-list*
  8.4861-    #+(or cmucl scl) extensions:*command-line-strings*
  8.4862-    #+gcl si:*command-args*
  8.4863-    #+(or genera mcl mezzano) nil
  8.4864-    #+lispworks sys:*line-arguments-list*
  8.4865-    #+mkcl (loop :for i :from 0 :below (mkcl:argc) :collect (mkcl:argv i))
  8.4866-    #+sbcl sb-ext:*posix-argv*
  8.4867-    #+xcl system:*argv*
  8.4868-    #-(or abcl allegro clasp clisp clozure cmucl ecl gcl genera lispworks mcl mezzano mkcl sbcl scl xcl)
  8.4869-    (not-implemented-error 'raw-command-line-arguments))
  8.4870-
  8.4871-  (defun command-line-arguments (&optional (arguments (raw-command-line-arguments)))
  8.4872-    "Extract user arguments from command-line invocation of current process.
  8.4873-Assume the calling conventions of a generated script that uses --
  8.4874-if we are not called from a directly executable image."
  8.4875-    (block nil
  8.4876-      #+abcl (return arguments)
  8.4877-      ;; SBCL and Allegro already separate user arguments from implementation arguments.
  8.4878-      #-(or sbcl allegro)
  8.4879-      (unless (eq *image-dumped-p* :executable)
  8.4880-        ;; LispWorks command-line processing isn't transparent to the user
  8.4881-        ;; unless you create a standalone executable; in that case,
  8.4882-        ;; we rely on cl-launch or some other script to set the arguments for us.
  8.4883-        #+lispworks (return *command-line-arguments*)
  8.4884-        ;; On other implementations, on non-standalone executables,
  8.4885-        ;; we trust cl-launch or whichever script starts the program
  8.4886-        ;; to use -- as a delimiter between implementation arguments and user arguments.
  8.4887-        #-lispworks (setf arguments (member "--" arguments :test 'string-equal)))
  8.4888-      (rest arguments)))
  8.4889-
  8.4890-  (defun argv0 ()
  8.4891-    "On supported implementations (most that matter), or when invoked by a proper wrapper script,
  8.4892-return a string that for the name with which the program was invoked, i.e. argv[0] in C.
  8.4893-Otherwise, return NIL."
  8.4894-    (cond
  8.4895-      ((eq *image-dumped-p* :executable) ; yes, this ARGV0 is our argv0 !
  8.4896-       ;; NB: not currently available on ABCL, Corman, Genera, MCL
  8.4897-       (or #+(or allegro clisp clozure cmucl gcl lispworks sbcl scl xcl)
  8.4898-           (first (raw-command-line-arguments))
  8.4899-           #+(or clasp ecl) (si:argv 0) #+mkcl (mkcl:argv 0)))
  8.4900-      (t ;; argv[0] is the name of the interpreter.
  8.4901-       ;; The wrapper script can export __CL_ARGV0. cl-launch does as of 4.0.1.8.
  8.4902-       (getenvp "__CL_ARGV0"))))
  8.4903-
  8.4904-  (defun setup-command-line-arguments ()
  8.4905-    (setf *command-line-arguments* (command-line-arguments)))
  8.4906-
  8.4907-  (defun restore-image (&key
  8.4908-                          (lisp-interaction *lisp-interaction*)
  8.4909-                          (restore-hook *image-restore-hook*)
  8.4910-                          (prelude *image-prelude*)
  8.4911-                          (entry-point *image-entry-point*)
  8.4912-                          (if-already-restored '(cerror "RUN RESTORE-IMAGE ANYWAY")))
  8.4913-    "From a freshly restarted Lisp image, restore the saved Lisp environment
  8.4914-by setting appropriate variables, running various hooks, and calling any specified entry point.
  8.4915-
  8.4916-If the image has already been restored or is already being restored, as per *IMAGE-RESTORED-P*,
  8.4917-call the IF-ALREADY-RESTORED error handler (by default, a continuable error), and do return
  8.4918-immediately to the surrounding restore process if allowed to continue.
  8.4919-
  8.4920-Then, comes the restore process itself:
  8.4921-First, call each function in the RESTORE-HOOK,
  8.4922-in the order they were registered with REGISTER-IMAGE-RESTORE-HOOK.
  8.4923-Second, evaluate the prelude, which is often Lisp text that is read,
  8.4924-as per EVAL-INPUT.
  8.4925-Third, call the ENTRY-POINT function, if any is specified, with no argument.
  8.4926-
  8.4927-The restore process happens in a WITH-FATAL-CONDITION-HANDLER, so that if LISP-INTERACTION is NIL,
  8.4928-any unhandled error leads to a backtrace and an exit with an error status.
  8.4929-If LISP-INTERACTION is NIL, the process also exits when no error occurs:
  8.4930-if neither restart nor entry function is provided, the program will exit with status 0 (success);
  8.4931-if a function was provided, the program will exit after the function returns (if it returns),
  8.4932-with status 0 if and only if the primary return value of result is generalized boolean true,
  8.4933-and with status 1 if this value is NIL.
  8.4934-
  8.4935-If LISP-INTERACTION is true, unhandled errors will take you to the debugger, and the result
  8.4936-of the function will be returned rather than interpreted as a boolean designating an exit code."
  8.4937-    (when *image-restored-p*
  8.4938-      (if if-already-restored
  8.4939-          (call-function if-already-restored "Image already ~:[being ~;~]restored"
  8.4940-                         (eq *image-restored-p* t))
  8.4941-          (return-from restore-image)))
  8.4942-    (with-fatal-condition-handler ()
  8.4943-      (setf *lisp-interaction* lisp-interaction)
  8.4944-      (setf *image-restore-hook* restore-hook)
  8.4945-      (setf *image-prelude* prelude)
  8.4946-      (setf *image-restored-p* :in-progress)
  8.4947-      (call-image-restore-hook)
  8.4948-      (standard-eval-thunk prelude)
  8.4949-      (setf *image-restored-p* t)
  8.4950-      (let ((results (multiple-value-list
  8.4951-                      (if entry-point
  8.4952-                          (call-function entry-point)
  8.4953-                          t))))
  8.4954-        (if lisp-interaction
  8.4955-            (values-list results)
  8.4956-            (shell-boolean-exit (first results)))))))
  8.4957-
  8.4958-
  8.4959-;;; Dumping an image
  8.4960-
  8.4961-(with-upgradability ()
  8.4962-  (defun dump-image (filename &key output-name executable
  8.4963-                                (postlude *image-postlude*)
  8.4964-                                (dump-hook *image-dump-hook*)
  8.4965-                                #+clozure prepend-symbols #+clozure (purify t)
  8.4966-                                #+sbcl compression
  8.4967-                                #+(and sbcl os-windows) application-type)
  8.4968-    "Dump an image of the current Lisp environment at pathname FILENAME, with various options.
  8.4969-
  8.4970-First, finalize the image, by evaluating the POSTLUDE as per EVAL-INPUT, then calling each of
  8.4971- the functions in DUMP-HOOK, in reverse order of registration by REGISTER-IMAGE-DUMP-HOOK.
  8.4972-
  8.4973-If EXECUTABLE is true, create an standalone executable program that calls RESTORE-IMAGE on startup.
  8.4974-
  8.4975-Pass various implementation-defined options, such as PREPEND-SYMBOLS and PURITY on CCL,
  8.4976-or COMPRESSION on SBCL, and APPLICATION-TYPE on SBCL/Windows."
  8.4977-    ;; Note: at least SBCL saves only global values of variables in the heap image,
  8.4978-    ;; so make sure things you want to dump are NOT just local bindings shadowing the global values.
  8.4979-    (declare (ignorable filename output-name executable))
  8.4980-    (setf *image-dumped-p* (if executable :executable t))
  8.4981-    (setf *image-restored-p* :in-regress)
  8.4982-    (setf *image-postlude* postlude)
  8.4983-    (standard-eval-thunk *image-postlude*)
  8.4984-    (setf *image-dump-hook* dump-hook)
  8.4985-    (call-image-dump-hook)
  8.4986-    (setf *image-restored-p* nil)
  8.4987-    #-(or clisp clozure (and cmucl executable) lispworks sbcl scl)
  8.4988-    (when executable
  8.4989-      (not-implemented-error 'dump-image "dumping an executable"))
  8.4990-    #+allegro
  8.4991-    (progn
  8.4992-      (sys:resize-areas :global-gc t :pack-heap t :sift-old-areas t :tenure t) ; :new 5000000
  8.4993-      (excl:dumplisp :name filename :suppress-allegro-cl-banner t))
  8.4994-    #+clisp
  8.4995-    (apply #'ext:saveinitmem filename
  8.4996-           :quiet t
  8.4997-           :start-package *package*
  8.4998-           :keep-global-handlers nil
  8.4999-           ;; Faré explains the odd executable value (slightly paraphrased):
  8.5000-           ;; 0 is very different from t in clisp and there for a good reason:
  8.5001-           ;; 0 turns the executable into one that has its own command-line handling, so hackers can't
  8.5002-           ;; use the underlying -i or -x to turn your would-be restricted binary into an unrestricted evaluator.
  8.5003-           :executable (if executable 0 t) ;--- requires clisp 2.48 or later, still catches --clisp-x
  8.5004-           (when executable
  8.5005-             (list
  8.5006-              ;; :parse-options nil ;--- requires a non-standard patch to clisp.
  8.5007-              :norc t :script nil :init-function #'restore-image)))
  8.5008-    #+clozure
  8.5009-    (flet ((dump (prepend-kernel)
  8.5010-             (ccl:save-application filename :prepend-kernel prepend-kernel :purify purify
  8.5011-                                            :toplevel-function (when executable #'restore-image))))
  8.5012-      ;;(setf ccl::*application* (make-instance 'ccl::lisp-development-system))
  8.5013-      (if prepend-symbols
  8.5014-          (with-temporary-file (:prefix "ccl-symbols-" :direction :output :pathname path)
  8.5015-            (require 'elf)
  8.5016-            (funcall (fdefinition 'ccl::write-elf-symbols-to-file) path)
  8.5017-            (dump path))
  8.5018-          (dump t)))
  8.5019-    #+(or cmucl scl)
  8.5020-    (progn
  8.5021-      (ext:gc :full t)
  8.5022-      (setf ext:*batch-mode* nil)
  8.5023-      (setf ext::*gc-run-time* 0)
  8.5024-      (apply 'ext:save-lisp filename
  8.5025-             :allow-other-keys t ;; hush SCL and old versions of CMUCL
  8.5026-             #+(and cmucl executable) :executable #+(and cmucl executable) t
  8.5027-             (when executable '(:init-function restore-image :process-command-line nil
  8.5028-                                :quiet t :load-init-file nil :site-init nil))))
  8.5029-    #+gcl
  8.5030-    (progn
  8.5031-      (si::set-hole-size 500) (si::gbc nil) (si::sgc-on t)
  8.5032-      (si::save-system filename))
  8.5033-    #+lispworks
  8.5034-    (if executable
  8.5035-        (lispworks:deliver 'restore-image filename 0 :interface nil)
  8.5036-        (hcl:save-image filename :environment nil))
  8.5037-    #+sbcl
  8.5038-    (progn
  8.5039-      ;;(sb-pcl::precompile-random-code-segments) ;--- it is ugly slow at compile-time (!) when the initial core is a big CLOS program. If you want it, do it yourself
  8.5040-      (setf sb-ext::*gc-run-time* 0)
  8.5041-      (apply 'sb-ext:save-lisp-and-die filename
  8.5042-             :executable t ;--- always include the runtime that goes with the core
  8.5043-             (append
  8.5044-              (when compression (list :compression compression))
  8.5045-              ;;--- only save runtime-options for standalone executables
  8.5046-              (when executable (list :toplevel #'restore-image :save-runtime-options t))
  8.5047-              #+(and sbcl os-windows) ;; passing :application-type :gui will disable the console window.
  8.5048-              ;; the default is :console - only works with SBCL 1.1.15 or later.
  8.5049-              (when application-type (list :application-type application-type)))))
  8.5050-    #-(or allegro clisp clozure cmucl gcl lispworks sbcl scl)
  8.5051-    (not-implemented-error 'dump-image))
  8.5052-
  8.5053-  (defun create-image (destination lisp-object-files
  8.5054-                       &key kind output-name prologue-code epilogue-code extra-object-files
  8.5055-                         (prelude () preludep) (postlude () postludep)
  8.5056-                         (entry-point () entry-point-p) build-args no-uiop)
  8.5057-    (declare (ignorable destination lisp-object-files extra-object-files kind output-name
  8.5058-                        prologue-code epilogue-code prelude preludep postlude postludep
  8.5059-                        entry-point entry-point-p build-args no-uiop))
  8.5060-    "On ECL, create an executable at pathname DESTINATION from the specified OBJECT-FILES and options"
  8.5061-    ;; Is it meaningful to run these in the current environment?
  8.5062-    ;; only if we also track the object files that constitute the "current" image,
  8.5063-    ;; and otherwise simulate dump-image, including quitting at the end.
  8.5064-    #-(or clasp ecl mkcl) (not-implemented-error 'create-image)
  8.5065-    #+(or clasp ecl mkcl)
  8.5066-    (let ((epilogue-code
  8.5067-           (if no-uiop
  8.5068-               epilogue-code
  8.5069-               (let ((forms
  8.5070-                      (append
  8.5071-                       (when epilogue-code `(,epilogue-code))
  8.5072-                       (when postludep `((setf *image-postlude* ',postlude)))
  8.5073-                       (when preludep `((setf *image-prelude* ',prelude)))
  8.5074-                       (when entry-point-p `((setf *image-entry-point* ',entry-point)))
  8.5075-                       (case kind
  8.5076-                         ((:image)
  8.5077-                          (setf kind :program) ;; to ECL, it's just another program.
  8.5078-                          `((setf *image-dumped-p* t)
  8.5079-                            (si::top-level #+(or clasp ecl) t) (quit)))
  8.5080-                         ((:program)
  8.5081-                          `((setf *image-dumped-p* :executable)
  8.5082-                            (shell-boolean-exit
  8.5083-                             (restore-image))))))))
  8.5084-                 (when forms `(progn ,@forms))))))
  8.5085-      (check-type kind (member :dll :shared-library :lib :static-library
  8.5086-                               :fasl :fasb :program))
  8.5087-      (apply #+clasp 'cmp:builder #+clasp kind
  8.5088-             #+(or ecl mkcl)
  8.5089-             (ecase kind
  8.5090-               ((:dll :shared-library)
  8.5091-                #+ecl 'c::build-shared-library #+mkcl 'compiler:build-shared-library)
  8.5092-               ((:lib :static-library)
  8.5093-                #+ecl 'c::build-static-library #+mkcl 'compiler:build-static-library)
  8.5094-               ((:fasl #+ecl :fasb)
  8.5095-                #+ecl 'c::build-fasl #+mkcl 'compiler:build-fasl)
  8.5096-               #+mkcl ((:fasb) 'compiler:build-bundle)
  8.5097-               ((:program)
  8.5098-                #+ecl 'c::build-program #+mkcl 'compiler:build-program))
  8.5099-             (pathname destination)
  8.5100-             #+(or clasp ecl) :lisp-files #+mkcl :lisp-object-files
  8.5101-             (append lisp-object-files #+(or clasp ecl) extra-object-files)
  8.5102-             #+ecl :init-name
  8.5103-             #+ecl (getf build-args :init-name)
  8.5104-             (append
  8.5105-              (when prologue-code `(:prologue-code ,prologue-code))
  8.5106-              (when epilogue-code `(:epilogue-code ,epilogue-code))
  8.5107-              #+mkcl (when extra-object-files `(:object-files ,extra-object-files))
  8.5108-              build-args)))))
  8.5109-
  8.5110-
  8.5111-;;; Some universal image restore hooks
  8.5112-(with-upgradability ()
  8.5113-  (map () 'register-image-restore-hook
  8.5114-       '(setup-stdin setup-stdout setup-stderr
  8.5115-         setup-command-line-arguments setup-temporary-directory
  8.5116-         #+abcl detect-os)))
  8.5117-;;;; -------------------------------------------------------------------------
  8.5118-;;;; Support to build (compile and load) Lisp files
  8.5119-
  8.5120-(uiop/package:define-package :uiop/lisp-build
  8.5121-  (:nicknames :asdf/lisp-build) ;; OBSOLETE, used by slime/contrib/swank-asdf.lisp
  8.5122-  (:use :uiop/common-lisp :uiop/package :uiop/utility
  8.5123-   :uiop/os :uiop/pathname :uiop/filesystem :uiop/stream :uiop/image)
  8.5124-  (:export
  8.5125-   ;; Variables
  8.5126-   #:*compile-file-warnings-behaviour* #:*compile-file-failure-behaviour*
  8.5127-   #:*output-translation-function*
  8.5128-   #:*optimization-settings* #:*previous-optimization-settings*
  8.5129-   #:*base-build-directory*
  8.5130-   #:compile-condition #:compile-file-error #:compile-warned-error #:compile-failed-error
  8.5131-   #:compile-warned-warning #:compile-failed-warning
  8.5132-   #:check-lisp-compile-results #:check-lisp-compile-warnings
  8.5133-   #:*uninteresting-conditions* #:*usual-uninteresting-conditions*
  8.5134-   #:*uninteresting-compiler-conditions* #:*uninteresting-loader-conditions*
  8.5135-   ;; Types
  8.5136-   #+sbcl #:sb-grovel-unknown-constant-condition
  8.5137-   ;; Functions & Macros
  8.5138-   #:get-optimization-settings #:proclaim-optimization-settings #:with-optimization-settings
  8.5139-   #:call-with-muffled-compiler-conditions #:with-muffled-compiler-conditions
  8.5140-   #:call-with-muffled-loader-conditions #:with-muffled-loader-conditions
  8.5141-   #:reify-simple-sexp #:unreify-simple-sexp
  8.5142-   #:reify-deferred-warnings #:unreify-deferred-warnings
  8.5143-   #:reset-deferred-warnings #:save-deferred-warnings #:check-deferred-warnings
  8.5144-   #:with-saved-deferred-warnings #:warnings-file-p #:warnings-file-type #:*warnings-file-type*
  8.5145-   #:enable-deferred-warnings-check #:disable-deferred-warnings-check
  8.5146-   #:current-lisp-file-pathname #:load-pathname
  8.5147-   #:lispize-pathname #:compile-file-type #:call-around-hook
  8.5148-   #:compile-file* #:compile-file-pathname* #:*compile-check*
  8.5149-   #:load* #:load-from-string #:combine-fasls)
  8.5150-  (:intern #:defaults #:failure-p #:warnings-p #:s #:y #:body))
  8.5151-(in-package :uiop/lisp-build)
  8.5152-
  8.5153-(with-upgradability ()
  8.5154-  (defvar *compile-file-warnings-behaviour*
  8.5155-    (or #+clisp :ignore :warn)
  8.5156-    "How should ASDF react if it encounters a warning when compiling a file?
  8.5157-Valid values are :error, :warn, and :ignore.")
  8.5158-
  8.5159-  (defvar *compile-file-failure-behaviour*
  8.5160-    (or #+(or mkcl sbcl) :error #+clisp :ignore :warn)
  8.5161-    "How should ASDF react if it encounters a failure (per the ANSI spec of COMPILE-FILE)
  8.5162-when compiling a file, which includes any non-style-warning warning.
  8.5163-Valid values are :error, :warn, and :ignore.
  8.5164-Note that ASDF ALWAYS raises an error if it fails to create an output file when compiling.")
  8.5165-
  8.5166-  (defvar *base-build-directory* nil
  8.5167-    "When set to a non-null value, it should be an absolute directory pathname,
  8.5168-which will serve as the *DEFAULT-PATHNAME-DEFAULTS* around a COMPILE-FILE,
  8.5169-what more while the input-file is shortened if possible to ENOUGH-PATHNAME relative to it.
  8.5170-This can help you produce more deterministic output for FASLs."))
  8.5171-
  8.5172-;;; Optimization settings
  8.5173-(with-upgradability ()
  8.5174-  (defvar *optimization-settings* nil
  8.5175-    "Optimization settings to be used by PROCLAIM-OPTIMIZATION-SETTINGS")
  8.5176-  (defvar *previous-optimization-settings* nil
  8.5177-    "Optimization settings saved by PROCLAIM-OPTIMIZATION-SETTINGS")
  8.5178-  (defparameter +optimization-variables+
  8.5179-    ;; TODO: allegro genera corman mcl
  8.5180-    (or #+(or abcl xcl) '(system::*speed* system::*space* system::*safety* system::*debug*)
  8.5181-        #+clisp '() ;; system::*optimize* is a constant hash-table! (with non-constant contents)
  8.5182-        #+clozure '(ccl::*nx-speed* ccl::*nx-space* ccl::*nx-safety*
  8.5183-                    ccl::*nx-debug* ccl::*nx-cspeed*)
  8.5184-        #+(or cmucl scl) '(c::*default-cookie*)
  8.5185-        #+clasp nil
  8.5186-        #+ecl (unless (use-ecl-byte-compiler-p) '(c::*speed* c::*space* c::*safety* c::*debug*))
  8.5187-        #+gcl '(compiler::*speed* compiler::*space* compiler::*compiler-new-safety* compiler::*debug*)
  8.5188-        #+lispworks '(compiler::*optimization-level*)
  8.5189-        #+mkcl '(si::*speed* si::*space* si::*safety* si::*debug*)
  8.5190-        #+sbcl '(sb-c::*policy*)))
  8.5191-  (defun get-optimization-settings ()
  8.5192-    "Get current compiler optimization settings, ready to PROCLAIM again"
  8.5193-    #-(or abcl allegro clasp clisp clozure cmucl ecl lispworks mkcl sbcl scl xcl)
  8.5194-    (warn "~S does not support ~S. Please help me fix that."
  8.5195-          'get-optimization-settings (implementation-type))
  8.5196-    #+clasp (cleavir-env:optimize (cleavir-env:optimize-info CLASP-CLEAVIR:*CLASP-ENV*))
  8.5197-    #+(or abcl allegro clisp clozure cmucl ecl lispworks mkcl sbcl scl xcl)
  8.5198-    (let ((settings '(speed space safety debug compilation-speed #+(or cmucl scl) c::brevity)))
  8.5199-      #.`(loop #+(or allegro clozure)
  8.5200-               ,@'(:with info = #+allegro (sys:declaration-information 'optimize)
  8.5201-                   #+clozure (ccl:declaration-information 'optimize nil))
  8.5202-               :for x :in settings
  8.5203-               ,@(or #+(or abcl clasp ecl gcl mkcl xcl) '(:for v :in +optimization-variables+))
  8.5204-               :for y = (or #+(or allegro clozure) (second (assoc x info)) ; normalize order
  8.5205-                            #+clisp (gethash x system::*optimize* 1)
  8.5206-                            #+(or abcl ecl mkcl xcl) (symbol-value v)
  8.5207-                            #+(or cmucl scl) (slot-value c::*default-cookie*
  8.5208-                                                       (case x (compilation-speed 'c::cspeed)
  8.5209-                                                             (otherwise x)))
  8.5210-                            #+lispworks (slot-value compiler::*optimization-level* x)
  8.5211-                            #+sbcl (sb-c::policy-quality sb-c::*policy* x))
  8.5212-               :when y :collect (list x y))))
  8.5213-  (defun proclaim-optimization-settings ()
  8.5214-    "Proclaim the optimization settings in *OPTIMIZATION-SETTINGS*"
  8.5215-    (proclaim `(optimize ,@*optimization-settings*))
  8.5216-    (let ((settings (get-optimization-settings)))
  8.5217-      (unless (equal *previous-optimization-settings* settings)
  8.5218-        (setf *previous-optimization-settings* settings))))
  8.5219-  (defmacro with-optimization-settings ((&optional (settings *optimization-settings*)) &body body)
  8.5220-    #+(or allegro clasp clisp)
  8.5221-    (let ((previous-settings (gensym "PREVIOUS-SETTINGS"))
  8.5222-          (reset-settings (gensym "RESET-SETTINGS")))
  8.5223-      `(let* ((,previous-settings (get-optimization-settings))
  8.5224-              (,reset-settings #+clasp (reverse ,previous-settings) #-clasp ,previous-settings))
  8.5225-         ,@(when settings `((proclaim `(optimize ,@,settings))))
  8.5226-         (unwind-protect (progn ,@body)
  8.5227-           (proclaim `(optimize ,@,reset-settings)))))
  8.5228-    #-(or allegro clasp clisp)
  8.5229-    `(let ,(loop :for v :in +optimization-variables+ :collect `(,v ,v))
  8.5230-       ,@(when settings `((proclaim `(optimize ,@,settings))))
  8.5231-       ,@body)))
  8.5232-
  8.5233-
  8.5234-;;; Condition control
  8.5235-(with-upgradability ()
  8.5236-  #+sbcl
  8.5237-  (progn
  8.5238-    (defun sb-grovel-unknown-constant-condition-p (c)
  8.5239-      "Detect SB-GROVEL unknown-constant conditions on older versions of SBCL"
  8.5240-      (ignore-errors
  8.5241-       (and (typep c 'sb-int:simple-style-warning)
  8.5242-            (string-enclosed-p
  8.5243-             "Couldn't grovel for "
  8.5244-             (simple-condition-format-control c)
  8.5245-             " (unknown to the C compiler)."))))
  8.5246-    (deftype sb-grovel-unknown-constant-condition ()
  8.5247-      '(and style-warning (satisfies sb-grovel-unknown-constant-condition-p))))
  8.5248-
  8.5249-  (defvar *usual-uninteresting-conditions*
  8.5250-    (append
  8.5251-     ;;#+clozure '(ccl:compiler-warning)
  8.5252-     #+cmucl '("Deleting unreachable code.")
  8.5253-     #+lispworks '("~S being redefined in ~A (previously in ~A)."
  8.5254-                   "~S defined more than once in ~A.") ;; lispworks gets confused by eval-when.
  8.5255-     #+sbcl
  8.5256-     '(sb-c::simple-compiler-note
  8.5257-       "&OPTIONAL and &KEY found in the same lambda list: ~S"
  8.5258-       sb-kernel:undefined-alien-style-warning
  8.5259-       sb-grovel-unknown-constant-condition ; defined above.
  8.5260-       sb-ext:implicit-generic-function-warning ;; Controversial.
  8.5261-       sb-int:package-at-variance
  8.5262-       sb-kernel:uninteresting-redefinition
  8.5263-       ;; BEWARE: the below four are controversial to include here.
  8.5264-       sb-kernel:redefinition-with-defun
  8.5265-       sb-kernel:redefinition-with-defgeneric
  8.5266-       sb-kernel:redefinition-with-defmethod
  8.5267-       sb-kernel::redefinition-with-defmacro) ; not exported by old SBCLs
  8.5268-     #+sbcl
  8.5269-     (let ((condition (find-symbol* '#:lexical-environment-too-complex :sb-kernel nil)))
  8.5270-       (when condition
  8.5271-         (list condition)))
  8.5272-     '("No generic function ~S present when encountering macroexpansion of defmethod. Assuming it will be an instance of standard-generic-function.")) ;; from closer2mop
  8.5273-    "A suggested value to which to set or bind *uninteresting-conditions*.")
  8.5274-
  8.5275-  (defvar *uninteresting-conditions* '()
  8.5276-    "Conditions that may be skipped while compiling or loading Lisp code.")
  8.5277-  (defvar *uninteresting-compiler-conditions* '()
  8.5278-    "Additional conditions that may be skipped while compiling Lisp code.")
  8.5279-  (defvar *uninteresting-loader-conditions*
  8.5280-    (append
  8.5281-     '("Overwriting already existing readtable ~S." ;; from named-readtables
  8.5282-       #(#:finalizers-off-warning :asdf-finalizers)) ;; from asdf-finalizers
  8.5283-     #+clisp '(clos::simple-gf-replacing-method-warning))
  8.5284-    "Additional conditions that may be skipped while loading Lisp code."))
  8.5285-
  8.5286-;;;; ----- Filtering conditions while building -----
  8.5287-(with-upgradability ()
  8.5288-  (defun call-with-muffled-compiler-conditions (thunk)
  8.5289-    "Call given THUNK in a context where uninteresting conditions and compiler conditions are muffled"
  8.5290-    (call-with-muffled-conditions
  8.5291-     thunk (append *uninteresting-conditions* *uninteresting-compiler-conditions*)))
  8.5292-  (defmacro with-muffled-compiler-conditions ((&optional) &body body)
  8.5293-    "Trivial syntax for CALL-WITH-MUFFLED-COMPILER-CONDITIONS"
  8.5294-    `(call-with-muffled-compiler-conditions #'(lambda () ,@body)))
  8.5295-  (defun call-with-muffled-loader-conditions (thunk)
  8.5296-    "Call given THUNK in a context where uninteresting conditions and loader conditions are muffled"
  8.5297-    (call-with-muffled-conditions
  8.5298-     thunk (append *uninteresting-conditions* *uninteresting-loader-conditions*)))
  8.5299-  (defmacro with-muffled-loader-conditions ((&optional) &body body)
  8.5300-    "Trivial syntax for CALL-WITH-MUFFLED-LOADER-CONDITIONS"
  8.5301-    `(call-with-muffled-loader-conditions #'(lambda () ,@body))))
  8.5302-
  8.5303-
  8.5304-;;;; Handle warnings and failures
  8.5305-(with-upgradability ()
  8.5306-  (define-condition compile-condition (condition)
  8.5307-    ((context-format
  8.5308-      :initform nil :reader compile-condition-context-format :initarg :context-format)
  8.5309-     (context-arguments
  8.5310-      :initform nil :reader compile-condition-context-arguments :initarg :context-arguments)
  8.5311-     (description
  8.5312-      :initform nil :reader compile-condition-description :initarg :description))
  8.5313-    (:report (lambda (c s)
  8.5314-               (format s (compatfmt "~@<~A~@[ while ~?~]~@:>")
  8.5315-                       (or (compile-condition-description c) (type-of c))
  8.5316-                       (compile-condition-context-format c)
  8.5317-                       (compile-condition-context-arguments c)))))
  8.5318-  (define-condition compile-file-error (compile-condition error) ())
  8.5319-  (define-condition compile-warned-warning (compile-condition warning) ())
  8.5320-  (define-condition compile-warned-error (compile-condition error) ())
  8.5321-  (define-condition compile-failed-warning (compile-condition warning) ())
  8.5322-  (define-condition compile-failed-error (compile-condition error) ())
  8.5323-
  8.5324-  (defun check-lisp-compile-warnings (warnings-p failure-p
  8.5325-                                                  &optional context-format context-arguments)
  8.5326-    "Given the warnings or failures as resulted from COMPILE-FILE or checking deferred warnings,
  8.5327-raise an error or warning as appropriate"
  8.5328-    (when failure-p
  8.5329-      (case *compile-file-failure-behaviour*
  8.5330-        (:warn (warn 'compile-failed-warning
  8.5331-                     :description "Lisp compilation failed"
  8.5332-                     :context-format context-format
  8.5333-                     :context-arguments context-arguments))
  8.5334-        (:error (error 'compile-failed-error
  8.5335-                       :description "Lisp compilation failed"
  8.5336-                       :context-format context-format
  8.5337-                       :context-arguments context-arguments))
  8.5338-        (:ignore nil)))
  8.5339-    (when warnings-p
  8.5340-      (case *compile-file-warnings-behaviour*
  8.5341-        (:warn (warn 'compile-warned-warning
  8.5342-                     :description "Lisp compilation had style-warnings"
  8.5343-                     :context-format context-format
  8.5344-                     :context-arguments context-arguments))
  8.5345-        (:error (error 'compile-warned-error
  8.5346-                       :description "Lisp compilation had style-warnings"
  8.5347-                       :context-format context-format
  8.5348-                       :context-arguments context-arguments))
  8.5349-        (:ignore nil))))
  8.5350-
  8.5351-  (defun check-lisp-compile-results (output warnings-p failure-p
  8.5352-                                             &optional context-format context-arguments)
  8.5353-    "Given the results of COMPILE-FILE, raise an error or warning as appropriate"
  8.5354-    (unless output
  8.5355-      (error 'compile-file-error :context-format context-format :context-arguments context-arguments))
  8.5356-    (check-lisp-compile-warnings warnings-p failure-p context-format context-arguments)))
  8.5357-
  8.5358-
  8.5359-;;;; Deferred-warnings treatment, originally implemented by Douglas Katzman.
  8.5360-;;;
  8.5361-;;; To support an implementation, three functions must be implemented:
  8.5362-;;; reify-deferred-warnings unreify-deferred-warnings reset-deferred-warnings
  8.5363-;;; See their respective docstrings.
  8.5364-(with-upgradability ()
  8.5365-  (defun reify-simple-sexp (sexp)
  8.5366-    "Given a simple SEXP, return a representation of it as a portable SEXP.
  8.5367-Simple means made of symbols, numbers, characters, simple-strings, pathnames, cons cells."
  8.5368-    (etypecase sexp
  8.5369-      (symbol (reify-symbol sexp))
  8.5370-      ((or number character simple-string pathname) sexp)
  8.5371-      (cons (cons (reify-simple-sexp (car sexp)) (reify-simple-sexp (cdr sexp))))
  8.5372-      (simple-vector (vector (mapcar 'reify-simple-sexp (coerce sexp 'list))))))
  8.5373-
  8.5374-  (defun unreify-simple-sexp (sexp)
  8.5375-    "Given the portable output of REIFY-SIMPLE-SEXP, return the simple SEXP it represents"
  8.5376-    (etypecase sexp
  8.5377-      ((or symbol number character simple-string pathname) sexp)
  8.5378-      (cons (cons (unreify-simple-sexp (car sexp)) (unreify-simple-sexp (cdr sexp))))
  8.5379-      ((simple-vector 2) (unreify-symbol sexp))
  8.5380-      ((simple-vector 1) (coerce (mapcar 'unreify-simple-sexp (aref sexp 0)) 'vector))))
  8.5381-
  8.5382-  #+clozure
  8.5383-  (progn
  8.5384-    (defun reify-source-note (source-note)
  8.5385-      (when source-note
  8.5386-        (with-accessors ((source ccl::source-note-source) (filename ccl:source-note-filename)
  8.5387-                         (start-pos ccl:source-note-start-pos) (end-pos ccl:source-note-end-pos)) source-note
  8.5388-          (declare (ignorable source))
  8.5389-          (list :filename filename :start-pos start-pos :end-pos end-pos
  8.5390-                #|:source (reify-source-note source)|#))))
  8.5391-    (defun unreify-source-note (source-note)
  8.5392-      (when source-note
  8.5393-        (destructuring-bind (&key filename start-pos end-pos source) source-note
  8.5394-          (ccl::make-source-note :filename filename :start-pos start-pos :end-pos end-pos
  8.5395-                                 :source (unreify-source-note source)))))
  8.5396-    (defun unsymbolify-function-name (name)
  8.5397-      (if-let (setfed (gethash name ccl::%setf-function-name-inverses%))
  8.5398-        `(setf ,setfed)
  8.5399-        name))
  8.5400-    (defun symbolify-function-name (name)
  8.5401-      (if (and (consp name) (eq (first name) 'setf))
  8.5402-          (let ((setfed (second name)))
  8.5403-            (gethash setfed ccl::%setf-function-names%))
  8.5404-          name))
  8.5405-    (defun reify-function-name (function-name)
  8.5406-      (let ((name (or (first function-name) ;; defun: extract the name
  8.5407-                      (let ((sec (second function-name)))
  8.5408-                        (or (and (atom sec) sec) ; scoped method: drop scope
  8.5409-                            (first sec)))))) ; method: keep gf name, drop method specializers
  8.5410-        (list name)))
  8.5411-    (defun unreify-function-name (function-name)
  8.5412-      function-name)
  8.5413-    (defun nullify-non-literals (sexp)
  8.5414-      (typecase sexp
  8.5415-        ((or number character simple-string symbol pathname) sexp)
  8.5416-        (cons (cons (nullify-non-literals (car sexp))
  8.5417-                    (nullify-non-literals (cdr sexp))))
  8.5418-        (t nil)))
  8.5419-    (defun reify-deferred-warning (deferred-warning)
  8.5420-      (with-accessors ((warning-type ccl::compiler-warning-warning-type)
  8.5421-                       (args ccl::compiler-warning-args)
  8.5422-                       (source-note ccl:compiler-warning-source-note)
  8.5423-                       (function-name ccl:compiler-warning-function-name)) deferred-warning
  8.5424-        (list :warning-type warning-type :function-name (reify-function-name function-name)
  8.5425-              :source-note (reify-source-note source-note)
  8.5426-              :args (destructuring-bind (fun &rest more)
  8.5427-                        args
  8.5428-                      (cons (unsymbolify-function-name fun)
  8.5429-                            (nullify-non-literals more))))))
  8.5430-    (defun unreify-deferred-warning (reified-deferred-warning)
  8.5431-      (destructuring-bind (&key warning-type function-name source-note args)
  8.5432-          reified-deferred-warning
  8.5433-        (make-condition (or (cdr (ccl::assq warning-type ccl::*compiler-whining-conditions*))
  8.5434-                            'ccl::compiler-warning)
  8.5435-                        :function-name (unreify-function-name function-name)
  8.5436-                        :source-note (unreify-source-note source-note)
  8.5437-                        :warning-type warning-type
  8.5438-                        :args (destructuring-bind (fun . more) args
  8.5439-                                (cons (symbolify-function-name fun) more))))))
  8.5440-  #+(or cmucl scl)
  8.5441-  (defun reify-undefined-warning (warning)
  8.5442-    ;; Extracting undefined-warnings from the compilation-unit
  8.5443-    ;; To be passed through the above reify/unreify link, it must be a "simple-sexp"
  8.5444-    (list*
  8.5445-     (c::undefined-warning-kind warning)
  8.5446-     (c::undefined-warning-name warning)
  8.5447-     (c::undefined-warning-count warning)
  8.5448-     (mapcar
  8.5449-      #'(lambda (frob)
  8.5450-          ;; the lexenv slot can be ignored for reporting purposes
  8.5451-          `(:enclosing-source ,(c::compiler-error-context-enclosing-source frob)
  8.5452-            :source ,(c::compiler-error-context-source frob)
  8.5453-            :original-source ,(c::compiler-error-context-original-source frob)
  8.5454-            :context ,(c::compiler-error-context-context frob)
  8.5455-            :file-name ,(c::compiler-error-context-file-name frob) ; a pathname
  8.5456-            :file-position ,(c::compiler-error-context-file-position frob) ; an integer
  8.5457-            :original-source-path ,(c::compiler-error-context-original-source-path frob)))
  8.5458-      (c::undefined-warning-warnings warning))))
  8.5459-
  8.5460-  #+sbcl
  8.5461-  (defun reify-undefined-warning (warning)
  8.5462-    ;; Extracting undefined-warnings from the compilation-unit
  8.5463-    ;; To be passed through the above reify/unreify link, it must be a "simple-sexp"
  8.5464-    (list*
  8.5465-     (sb-c::undefined-warning-kind warning)
  8.5466-     (sb-c::undefined-warning-name warning)
  8.5467-     (sb-c::undefined-warning-count warning)
  8.5468-     ;; the COMPILER-ERROR-CONTEXT struct has changed in SBCL, which means how we
  8.5469-     ;; handle deferred warnings must change... TODO: when enough time has
  8.5470-     ;; gone by, just assume all versions of SBCL are adequately
  8.5471-     ;; up-to-date, and cut this material.[2018/05/30:rpg]
  8.5472-     (mapcar
  8.5473-      #'(lambda (frob)
  8.5474-          ;; the lexenv slot can be ignored for reporting purposes
  8.5475-          `(
  8.5476-            #- #.(uiop/utility:symbol-test-to-feature-expression '#:compiler-error-context-%source '#:sb-c)
  8.5477-            ,@`(:enclosing-source
  8.5478-                ,(sb-c::compiler-error-context-enclosing-source frob)
  8.5479-                :source
  8.5480-                ,(sb-c::compiler-error-context-source frob)
  8.5481-                :original-source
  8.5482-                ,(sb-c::compiler-error-context-original-source frob))
  8.5483-            #+ #.(uiop/utility:symbol-test-to-feature-expression '#:compiler-error-context-%source '#:sb-c)
  8.5484-            ,@ `(:%enclosing-source
  8.5485-                 ,(sb-c::compiler-error-context-enclosing-source frob)
  8.5486-                 :%source
  8.5487-                 ,(sb-c::compiler-error-context-source frob)
  8.5488-                 :original-form
  8.5489-                 ,(sb-c::compiler-error-context-original-form frob))
  8.5490-            :context ,(sb-c::compiler-error-context-context frob)
  8.5491-            :file-name ,(sb-c::compiler-error-context-file-name frob) ; a pathname
  8.5492-            :file-position ,(sb-c::compiler-error-context-file-position frob) ; an integer
  8.5493-            :original-source-path ,(sb-c::compiler-error-context-original-source-path frob)))
  8.5494-      (sb-c::undefined-warning-warnings warning))))
  8.5495-
  8.5496-  (defun reify-deferred-warnings ()
  8.5497-    "return a portable S-expression, portably readable and writeable in any Common Lisp implementation
  8.5498-using READ within a WITH-SAFE-IO-SYNTAX, that represents the warnings currently deferred by
  8.5499-WITH-COMPILATION-UNIT. One of three functions required for deferred-warnings support in ASDF."
  8.5500-    #+allegro
  8.5501-    (list :functions-defined excl::.functions-defined.
  8.5502-          :functions-called excl::.functions-called.)
  8.5503-    #+clozure
  8.5504-    (mapcar 'reify-deferred-warning
  8.5505-            (if-let (dw ccl::*outstanding-deferred-warnings*)
  8.5506-              (let ((mdw (ccl::ensure-merged-deferred-warnings dw)))
  8.5507-                (ccl::deferred-warnings.warnings mdw))))
  8.5508-    #+(or cmucl scl)
  8.5509-    (when lisp::*in-compilation-unit*
  8.5510-      ;; Try to send nothing through the pipe if nothing needs to be accumulated
  8.5511-      `(,@(when c::*undefined-warnings*
  8.5512-            `((c::*undefined-warnings*
  8.5513-               ,@(mapcar #'reify-undefined-warning c::*undefined-warnings*))))
  8.5514-        ,@(loop :for what :in '(c::*compiler-error-count*
  8.5515-                                c::*compiler-warning-count*
  8.5516-                                c::*compiler-note-count*)
  8.5517-                :for value = (symbol-value what)
  8.5518-                :when (plusp value)
  8.5519-                  :collect `(,what . ,value))))
  8.5520-    #+sbcl
  8.5521-    (when sb-c::*in-compilation-unit*
  8.5522-      ;; Try to send nothing through the pipe if nothing needs to be accumulated
  8.5523-      `(,@(when sb-c::*undefined-warnings*
  8.5524-            `((sb-c::*undefined-warnings*
  8.5525-               ,@(mapcar #'reify-undefined-warning sb-c::*undefined-warnings*))))
  8.5526-        ,@(loop :for what :in '(sb-c::*aborted-compilation-unit-count*
  8.5527-                                sb-c::*compiler-error-count*
  8.5528-                                sb-c::*compiler-warning-count*
  8.5529-                                sb-c::*compiler-style-warning-count*
  8.5530-                                sb-c::*compiler-note-count*)
  8.5531-                :for value = (symbol-value what)
  8.5532-                :when (plusp value)
  8.5533-                  :collect `(,what . ,value)))))
  8.5534-
  8.5535-  (defun unreify-deferred-warnings (reified-deferred-warnings)
  8.5536-    "given a S-expression created by REIFY-DEFERRED-WARNINGS, reinstantiate the corresponding
  8.5537-deferred warnings as to be handled at the end of the current WITH-COMPILATION-UNIT.
  8.5538-Handle any warning that has been resolved already,
  8.5539-such as an undefined function that has been defined since.
  8.5540-One of three functions required for deferred-warnings support in ASDF."
  8.5541-    (declare (ignorable reified-deferred-warnings))
  8.5542-    #+allegro
  8.5543-    (destructuring-bind (&key functions-defined functions-called)
  8.5544-        reified-deferred-warnings
  8.5545-      (setf excl::.functions-defined.
  8.5546-            (append functions-defined excl::.functions-defined.)
  8.5547-            excl::.functions-called.
  8.5548-            (append functions-called excl::.functions-called.)))
  8.5549-    #+clozure
  8.5550-    (let ((dw (or ccl::*outstanding-deferred-warnings*
  8.5551-                  (setf ccl::*outstanding-deferred-warnings* (ccl::%defer-warnings t)))))
  8.5552-      (appendf (ccl::deferred-warnings.warnings dw)
  8.5553-               (mapcar 'unreify-deferred-warning reified-deferred-warnings)))
  8.5554-    #+(or cmucl scl)
  8.5555-    (dolist (item reified-deferred-warnings)
  8.5556-      ;; Each item is (symbol . adjustment) where the adjustment depends on the symbol.
  8.5557-      ;; For *undefined-warnings*, the adjustment is a list of initargs.
  8.5558-      ;; For everything else, it's an integer.
  8.5559-      (destructuring-bind (symbol . adjustment) item
  8.5560-        (case symbol
  8.5561-          ((c::*undefined-warnings*)
  8.5562-           (setf c::*undefined-warnings*
  8.5563-                 (nconc (mapcan
  8.5564-                         #'(lambda (stuff)
  8.5565-                             (destructuring-bind (kind name count . rest) stuff
  8.5566-                               (unless (case kind (:function (fboundp name)))
  8.5567-                                 (list
  8.5568-                                  (c::make-undefined-warning
  8.5569-                                   :name name
  8.5570-                                   :kind kind
  8.5571-                                   :count count
  8.5572-                                   :warnings
  8.5573-                                   (mapcar #'(lambda (x)
  8.5574-                                               (apply #'c::make-compiler-error-context x))
  8.5575-                                           rest))))))
  8.5576-                         adjustment)
  8.5577-                        c::*undefined-warnings*)))
  8.5578-          (otherwise
  8.5579-           (set symbol (+ (symbol-value symbol) adjustment))))))
  8.5580-    #+sbcl
  8.5581-    (dolist (item reified-deferred-warnings)
  8.5582-      ;; Each item is (symbol . adjustment) where the adjustment depends on the symbol.
  8.5583-      ;; For *undefined-warnings*, the adjustment is a list of initargs.
  8.5584-      ;; For everything else, it's an integer.
  8.5585-      (destructuring-bind (symbol . adjustment) item
  8.5586-        (case symbol
  8.5587-          ((sb-c::*undefined-warnings*)
  8.5588-           (setf sb-c::*undefined-warnings*
  8.5589-                 (nconc (mapcan
  8.5590-                         #'(lambda (stuff)
  8.5591-                             (destructuring-bind (kind name count . rest) stuff
  8.5592-                               (unless (case kind (:function (fboundp name)))
  8.5593-                                 (list
  8.5594-                                  (sb-c::make-undefined-warning
  8.5595-                                   :name name
  8.5596-                                   :kind kind
  8.5597-                                   :count count
  8.5598-                                   :warnings
  8.5599-                                   (mapcar #'(lambda (x)
  8.5600-                                               (apply #'sb-c::make-compiler-error-context x))
  8.5601-                                           rest))))))
  8.5602-                         adjustment)
  8.5603-                        sb-c::*undefined-warnings*)))
  8.5604-          (otherwise
  8.5605-           (set symbol (+ (symbol-value symbol) adjustment)))))))
  8.5606-
  8.5607-  (defun reset-deferred-warnings ()
  8.5608-    "Reset the set of deferred warnings to be handled at the end of the current WITH-COMPILATION-UNIT.
  8.5609-One of three functions required for deferred-warnings support in ASDF."
  8.5610-    #+allegro
  8.5611-    (setf excl::.functions-defined. nil
  8.5612-          excl::.functions-called. nil)
  8.5613-    #+clozure
  8.5614-    (if-let (dw ccl::*outstanding-deferred-warnings*)
  8.5615-      (let ((mdw (ccl::ensure-merged-deferred-warnings dw)))
  8.5616-        (setf (ccl::deferred-warnings.warnings mdw) nil)))
  8.5617-    #+(or cmucl scl)
  8.5618-    (when lisp::*in-compilation-unit*
  8.5619-      (setf c::*undefined-warnings* nil
  8.5620-            c::*compiler-error-count* 0
  8.5621-            c::*compiler-warning-count* 0
  8.5622-            c::*compiler-note-count* 0))
  8.5623-    #+sbcl
  8.5624-    (when sb-c::*in-compilation-unit*
  8.5625-      (setf sb-c::*undefined-warnings* nil
  8.5626-            sb-c::*aborted-compilation-unit-count* 0
  8.5627-            sb-c::*compiler-error-count* 0
  8.5628-            sb-c::*compiler-warning-count* 0
  8.5629-            sb-c::*compiler-style-warning-count* 0
  8.5630-            sb-c::*compiler-note-count* 0)))
  8.5631-
  8.5632-  (defun save-deferred-warnings (warnings-file)
  8.5633-    "Save forward reference conditions so they may be issued at a latter time,
  8.5634-possibly in a different process."
  8.5635-    (with-open-file (s warnings-file :direction :output :if-exists :supersede
  8.5636-                       :element-type *default-stream-element-type*
  8.5637-                       :external-format *utf-8-external-format*)
  8.5638-      (with-safe-io-syntax ()
  8.5639-        (let ((*read-eval* t))
  8.5640-          (write (reify-deferred-warnings) :stream s :pretty t :readably t))
  8.5641-        (terpri s))))
  8.5642-
  8.5643-  (defun warnings-file-type (&optional implementation-type)
  8.5644-    "The pathname type for warnings files on given IMPLEMENTATION-TYPE,
  8.5645-where NIL designates the current one"
  8.5646-    (case (or implementation-type *implementation-type*)
  8.5647-      ((:acl :allegro) "allegro-warnings")
  8.5648-      ;;((:clisp) "clisp-warnings")
  8.5649-      ((:cmu :cmucl) "cmucl-warnings")
  8.5650-      ((:sbcl) "sbcl-warnings")
  8.5651-      ((:clozure :ccl) "ccl-warnings")
  8.5652-      ((:scl) "scl-warnings")))
  8.5653-
  8.5654-  (defvar *warnings-file-type* nil
  8.5655-    "Pathname type for warnings files, or NIL if disabled")
  8.5656-
  8.5657-  (defun enable-deferred-warnings-check ()
  8.5658-    "Enable the saving of deferred warnings"
  8.5659-    (setf *warnings-file-type* (warnings-file-type)))
  8.5660-
  8.5661-  (defun disable-deferred-warnings-check ()
  8.5662-    "Disable the saving of deferred warnings"
  8.5663-    (setf *warnings-file-type* nil))
  8.5664-
  8.5665-  (defun warnings-file-p (file &optional implementation-type)
  8.5666-    "Is FILE a saved warnings file for the given IMPLEMENTATION-TYPE?
  8.5667-If that given type is NIL, use the currently configured *WARNINGS-FILE-TYPE* instead."
  8.5668-    (if-let (type (if implementation-type
  8.5669-                      (warnings-file-type implementation-type)
  8.5670-                      *warnings-file-type*))
  8.5671-      (equal (pathname-type file) type)))
  8.5672-
  8.5673-  (defun check-deferred-warnings (files &optional context-format context-arguments)
  8.5674-    "Given a list of FILES containing deferred warnings saved by CALL-WITH-SAVED-DEFERRED-WARNINGS,
  8.5675-re-intern and raise any warnings that are still meaningful."
  8.5676-    (let ((file-errors nil)
  8.5677-          (failure-p nil)
  8.5678-          (warnings-p nil))
  8.5679-      (handler-bind
  8.5680-          ((warning #'(lambda (c)
  8.5681-                        (setf warnings-p t)
  8.5682-                        (unless (typep c 'style-warning)
  8.5683-                          (setf failure-p t)))))
  8.5684-        (with-compilation-unit (:override t)
  8.5685-          (reset-deferred-warnings)
  8.5686-          (dolist (file files)
  8.5687-            (unreify-deferred-warnings
  8.5688-             (handler-case
  8.5689-                 (with-safe-io-syntax ()
  8.5690-                   (let ((*read-eval* t))
  8.5691-                     (read-file-form file)))
  8.5692-               (error (c)
  8.5693-                 ;;(delete-file-if-exists file) ;; deleting forces rebuild but prevents debugging
  8.5694-                 (push c file-errors)
  8.5695-                 nil))))))
  8.5696-      (dolist (error file-errors) (error error))
  8.5697-      (check-lisp-compile-warnings
  8.5698-       (or failure-p warnings-p) failure-p context-format context-arguments)))
  8.5699-
  8.5700-  #|
  8.5701-  Mini-guide to adding support for deferred warnings on an implementation.
  8.5702-
  8.5703-  First, look at what such a warning looks like:
  8.5704-
  8.5705-  (describe
  8.5706-  (handler-case
  8.5707-  (and (eval '(lambda () (some-undefined-function))) nil)
  8.5708-  (t (c) c)))
  8.5709-
  8.5710-  Then you can grep for the condition type in your compiler sources
  8.5711-  and see how to catch those that have been deferred,
  8.5712-  and/or read, clear and restore the deferred list.
  8.5713-
  8.5714-  Also look at
  8.5715-  (macroexpand-1 '(with-compilation-unit () foo))
  8.5716-  |#
  8.5717-
  8.5718-  (defun call-with-saved-deferred-warnings (thunk warnings-file &key source-namestring)
  8.5719-    "If WARNINGS-FILE is not nil, record the deferred-warnings around a call to THUNK
  8.5720-and save those warnings to the given file for latter use,
  8.5721-possibly in a different process. Otherwise just call THUNK."
  8.5722-    (declare (ignorable source-namestring))
  8.5723-    (if warnings-file
  8.5724-        (with-compilation-unit (:override t #+sbcl :source-namestring #+sbcl source-namestring)
  8.5725-          (unwind-protect
  8.5726-               (let (#+sbcl (sb-c::*undefined-warnings* nil))
  8.5727-                 (multiple-value-prog1
  8.5728-                     (funcall thunk)
  8.5729-                   (save-deferred-warnings warnings-file)))
  8.5730-            (reset-deferred-warnings)))
  8.5731-        (funcall thunk)))
  8.5732-
  8.5733-  (defmacro with-saved-deferred-warnings ((warnings-file &key source-namestring) &body body)
  8.5734-    "Trivial syntax for CALL-WITH-SAVED-DEFERRED-WARNINGS"
  8.5735-    `(call-with-saved-deferred-warnings
  8.5736-      #'(lambda () ,@body) ,warnings-file :source-namestring ,source-namestring)))
  8.5737-
  8.5738-
  8.5739-;;; from ASDF
  8.5740-(with-upgradability ()
  8.5741-  (defun current-lisp-file-pathname ()
  8.5742-    "Portably return the PATHNAME of the current Lisp source file being compiled or loaded"
  8.5743-    (or *compile-file-pathname* *load-pathname*))
  8.5744-
  8.5745-  (defun load-pathname ()
  8.5746-    "Portably return the LOAD-PATHNAME of the current source file or fasl.
  8.5747-    May return a relative pathname."
  8.5748-    *load-pathname*) ;; magic no longer needed for GCL.
  8.5749-
  8.5750-  (defun lispize-pathname (input-file)
  8.5751-    "From a INPUT-FILE pathname, return a corresponding .lisp source pathname"
  8.5752-    (make-pathname :type "lisp" :defaults input-file))
  8.5753-
  8.5754-  (defun compile-file-type (&rest keys)
  8.5755-    "pathname TYPE for lisp FASt Loading files"
  8.5756-    (declare (ignorable keys))
  8.5757-    #-(or clasp ecl mkcl) (load-time-value (pathname-type (compile-file-pathname "foo.lisp")))
  8.5758-    #+(or clasp ecl mkcl) (pathname-type (apply 'compile-file-pathname "foo" keys)))
  8.5759-
  8.5760-  (defun call-around-hook (hook function)
  8.5761-    "Call a HOOK around the execution of FUNCTION"
  8.5762-    (call-function (or hook 'funcall) function))
  8.5763-
  8.5764-  (defun compile-file-pathname* (input-file &rest keys &key output-file &allow-other-keys)
  8.5765-    "Variant of COMPILE-FILE-PATHNAME that works well with COMPILE-FILE*"
  8.5766-    (let* ((keys
  8.5767-             (remove-plist-keys `(#+(or (and allegro (not (version>= 8 2)))) :external-format
  8.5768-                                    ,@(unless output-file '(:output-file))) keys)))
  8.5769-      (if (absolute-pathname-p output-file)
  8.5770-          ;; what cfp should be doing, w/ mp* instead of mp
  8.5771-          (let* ((type (pathname-type (apply 'compile-file-type keys)))
  8.5772-                 (defaults (make-pathname
  8.5773-                            :type type :defaults (merge-pathnames* input-file))))
  8.5774-            (merge-pathnames* output-file defaults))
  8.5775-          (funcall *output-translation-function*
  8.5776-                   (apply 'compile-file-pathname input-file keys)))))
  8.5777-
  8.5778-  (defvar *compile-check* nil
  8.5779-    "A hook for user-defined compile-time invariants")
  8.5780-
  8.5781-  (defun compile-file* (input-file &rest keys
  8.5782-                                      &key (compile-check *compile-check*) output-file warnings-file
  8.5783-                                      #+clisp lib-file #+(or clasp ecl mkcl) object-file #+sbcl emit-cfasl
  8.5784-                                      &allow-other-keys)
  8.5785-    "This function provides a portable wrapper around COMPILE-FILE.
  8.5786-It ensures that the OUTPUT-FILE value is only returned and
  8.5787-the file only actually created if the compilation was successful,
  8.5788-even though your implementation may not do that. It also checks an optional
  8.5789-user-provided consistency function COMPILE-CHECK to determine success;
  8.5790-it will call this function if not NIL at the end of the compilation
  8.5791-with the arguments sent to COMPILE-FILE*, except with :OUTPUT-FILE TMP-FILE
  8.5792-where TMP-FILE is the name of a temporary output-file.
  8.5793-It also checks two flags (with legacy british spelling from ASDF1),
  8.5794-*COMPILE-FILE-FAILURE-BEHAVIOUR* and *COMPILE-FILE-WARNINGS-BEHAVIOUR*
  8.5795-with appropriate implementation-dependent defaults,
  8.5796-and if a failure (respectively warnings) are reported by COMPILE-FILE,
  8.5797-it will consider that an error unless the respective behaviour flag
  8.5798-is one of :SUCCESS :WARN :IGNORE.
  8.5799-If WARNINGS-FILE is defined, deferred warnings are saved to that file.
  8.5800-On ECL or MKCL, it creates both the linkable object and loadable fasl files.
  8.5801-On implementations that erroneously do not recognize standard keyword arguments,
  8.5802-it will filter them appropriately."
  8.5803-    #+(or clasp ecl)
  8.5804-    (when (and object-file (equal (compile-file-type) (pathname object-file)))
  8.5805-      (format t "Whoa, some funky ASDF upgrade switched ~S calling convention for ~S and ~S~%"
  8.5806-              'compile-file* output-file object-file)
  8.5807-      (rotatef output-file object-file))
  8.5808-    (let* ((keywords (remove-plist-keys
  8.5809-                      `(:output-file :compile-check :warnings-file
  8.5810-                                     #+clisp :lib-file #+(or clasp ecl mkcl) :object-file) keys))
  8.5811-           (output-file
  8.5812-             (or output-file
  8.5813-                 (apply 'compile-file-pathname* input-file :output-file output-file keywords)))
  8.5814-           (physical-output-file (physicalize-pathname output-file))
  8.5815-           #+(or clasp ecl)
  8.5816-           (object-file
  8.5817-             (unless (use-ecl-byte-compiler-p)
  8.5818-               (or object-file
  8.5819-                   #+ecl (compile-file-pathname output-file :type :object)
  8.5820-                   #+clasp (compile-file-pathname output-file :output-type :object))))
  8.5821-           #+mkcl
  8.5822-           (object-file
  8.5823-             (or object-file
  8.5824-                 (compile-file-pathname output-file :fasl-p nil)))
  8.5825-           (tmp-file (tmpize-pathname physical-output-file))
  8.5826-           #+clasp
  8.5827-           (tmp-object-file (compile-file-pathname tmp-file :output-type :object))
  8.5828-           #+sbcl
  8.5829-           (cfasl-file (etypecase emit-cfasl
  8.5830-                         (null nil)
  8.5831-                         ((eql t) (make-pathname :type "cfasl" :defaults physical-output-file))
  8.5832-                         (string (parse-namestring emit-cfasl))
  8.5833-                         (pathname emit-cfasl)))
  8.5834-           #+sbcl
  8.5835-           (tmp-cfasl (when cfasl-file (make-pathname :type "cfasl" :defaults tmp-file)))
  8.5836-           #+clisp
  8.5837-           (tmp-lib (make-pathname :type "lib" :defaults tmp-file)))
  8.5838-      (multiple-value-bind (output-truename warnings-p failure-p)
  8.5839-          (with-enough-pathname (input-file :defaults *base-build-directory*)
  8.5840-            (with-saved-deferred-warnings (warnings-file :source-namestring (namestring input-file))
  8.5841-              (with-muffled-compiler-conditions ()
  8.5842-                (or #-(or clasp ecl mkcl)
  8.5843-                    (let (#+genera (si:*common-lisp-syntax-is-ansi-common-lisp* t))
  8.5844-                      (apply 'compile-file input-file :output-file tmp-file
  8.5845-                             #+sbcl (if emit-cfasl (list* :emit-cfasl tmp-cfasl keywords) keywords)
  8.5846-                             #-sbcl keywords))
  8.5847-                    #+ecl (apply 'compile-file input-file :output-file
  8.5848-                                (if object-file
  8.5849-                                    (list* object-file :system-p t keywords)
  8.5850-                                    (list* tmp-file keywords)))
  8.5851-                    #+clasp (apply 'compile-file input-file :output-file
  8.5852-                                  (if object-file
  8.5853-                                      (list* tmp-object-file :output-type :object #|:system-p t|# keywords)
  8.5854-                                      (list* tmp-file keywords)))
  8.5855-                    #+mkcl (apply 'compile-file input-file
  8.5856-                                  :output-file object-file :fasl-p nil keywords)))))
  8.5857-        (cond
  8.5858-          ((and output-truename
  8.5859-                (flet ((check-flag (flag behaviour)
  8.5860-                         (or (not flag) (member behaviour '(:success :warn :ignore)))))
  8.5861-                  (and (check-flag failure-p *compile-file-failure-behaviour*)
  8.5862-                       (check-flag warnings-p *compile-file-warnings-behaviour*)))
  8.5863-                (progn
  8.5864-                  #+(or clasp ecl mkcl)
  8.5865-                  (when (and #+(or clasp ecl) object-file)
  8.5866-                    (setf output-truename
  8.5867-                          (compiler::build-fasl tmp-file
  8.5868-                           #+(or clasp ecl) :lisp-files #+mkcl :lisp-object-files (list #+clasp tmp-object-file #-clasp object-file))))
  8.5869-                  (or (not compile-check)
  8.5870-                      (apply compile-check input-file
  8.5871-                             :output-file output-truename
  8.5872-                             keywords))))
  8.5873-           (delete-file-if-exists physical-output-file)
  8.5874-           (when output-truename
  8.5875-             ;; see CLISP bug 677
  8.5876-             #+clisp
  8.5877-             (progn
  8.5878-               (setf tmp-lib (make-pathname :type "lib" :defaults output-truename))
  8.5879-               (unless lib-file (setf lib-file (make-pathname :type "lib" :defaults physical-output-file)))
  8.5880-               (rename-file-overwriting-target tmp-lib lib-file))
  8.5881-             #+sbcl (when cfasl-file (rename-file-overwriting-target tmp-cfasl cfasl-file))
  8.5882-             #+clasp
  8.5883-             (progn
  8.5884-               ;;; the following 4 rename-file-overwriting-target better be atomic, but we can't implement this right now
  8.5885-               #+:target-os-darwin
  8.5886-               (let ((temp-dwarf (pathname (strcat (namestring output-truename) ".dwarf")))
  8.5887-                     (target-dwarf (pathname (strcat (namestring physical-output-file) ".dwarf"))))
  8.5888-                 (when (probe-file temp-dwarf)
  8.5889-                   (rename-file-overwriting-target temp-dwarf target-dwarf)))
  8.5890-               ;;; need to rename the bc or ll file as well or test-bundle.script fails
  8.5891-               ;;; They might not exist with parallel compilation
  8.5892-               (let ((bitcode-src (compile-file-pathname tmp-file :output-type :bitcode))
  8.5893-                     (bitcode-target (compile-file-pathname physical-output-file :output-type :bitcode)))
  8.5894-                 (when (probe-file bitcode-src)
  8.5895-                   (rename-file-overwriting-target bitcode-src bitcode-target)))
  8.5896-               (rename-file-overwriting-target tmp-object-file object-file))
  8.5897-             (rename-file-overwriting-target output-truename physical-output-file)
  8.5898-             (setf output-truename (truename physical-output-file)))
  8.5899-           #+clasp (delete-file-if-exists tmp-file)
  8.5900-           #+clisp (progn (delete-file-if-exists tmp-file) ;; this one works around clisp BUG 677
  8.5901-                          (delete-file-if-exists tmp-lib))) ;; this one is "normal" defensive cleanup
  8.5902-          (t ;; error or failed check
  8.5903-           (delete-file-if-exists output-truename)
  8.5904-           #+clisp (delete-file-if-exists tmp-lib)
  8.5905-           #+sbcl (delete-file-if-exists tmp-cfasl)
  8.5906-           (setf output-truename nil)))
  8.5907-        (values output-truename warnings-p failure-p))))
  8.5908-
  8.5909-  (defun load* (x &rest keys &key &allow-other-keys)
  8.5910-    "Portable wrapper around LOAD that properly handles loading from a stream."
  8.5911-    (with-muffled-loader-conditions ()
  8.5912-      (let (#+genera (si:*common-lisp-syntax-is-ansi-common-lisp* t))
  8.5913-        (etypecase x
  8.5914-          ((or pathname string #-(or allegro clozure genera) stream #+clozure file-stream)
  8.5915-           (apply 'load x keys))
  8.5916-          ;; Genera can't load from a string-input-stream
  8.5917-          ;; ClozureCL 1.6 can only load from file input stream
  8.5918-          ;; Allegro 5, I don't remember but it must have been broken when I tested.
  8.5919-          #+(or allegro clozure genera)
  8.5920-          (stream ;; make do this way
  8.5921-           (let ((*package* *package*)
  8.5922-                 (*readtable* *readtable*)
  8.5923-                 (*load-pathname* nil)
  8.5924-                 (*load-truename* nil))
  8.5925-             (eval-input x)))))))
  8.5926-
  8.5927-  (defun load-from-string (string)
  8.5928-    "Portably read and evaluate forms from a STRING."
  8.5929-    (with-input-from-string (s string) (load* s))))
  8.5930-
  8.5931-;;; Links FASLs together
  8.5932-(with-upgradability ()
  8.5933-  (defun combine-fasls (inputs output)
  8.5934-    "Combine a list of FASLs INPUTS into a single FASL OUTPUT"
  8.5935-    #-(or abcl allegro clisp clozure cmucl lispworks sbcl scl xcl)
  8.5936-    (not-implemented-error 'combine-fasls "~%inputs: ~S~%output: ~S" inputs output)
  8.5937-    #+abcl (funcall 'sys::concatenate-fasls inputs output) ; requires ABCL 1.2.0
  8.5938-    #+(or allegro clisp cmucl sbcl scl xcl) (concatenate-files inputs output)
  8.5939-    #+clozure (ccl:fasl-concatenate output inputs :if-exists :supersede)
  8.5940-    #+lispworks
  8.5941-    (let (fasls)
  8.5942-      (unwind-protect
  8.5943-           (progn
  8.5944-             (loop :for i :in inputs
  8.5945-                   :for n :from 1
  8.5946-                   :for f = (add-pathname-suffix
  8.5947-                             output (format nil "-FASL~D" n))
  8.5948-                   :do (copy-file i f)
  8.5949-                       (push f fasls))
  8.5950-             (ignore-errors (lispworks:delete-system :fasls-to-concatenate))
  8.5951-             (eval `(scm:defsystem :fasls-to-concatenate
  8.5952-                      (:default-pathname ,(pathname-directory-pathname output))
  8.5953-                      :members
  8.5954-                      ,(loop :for f :in (reverse fasls)
  8.5955-                             :collect `(,(namestring f) :load-only t))))
  8.5956-             (scm:concatenate-system output :fasls-to-concatenate :force t))
  8.5957-        (loop :for f :in fasls :do (ignore-errors (delete-file f)))
  8.5958-        (ignore-errors (lispworks:delete-system :fasls-to-concatenate))))))
  8.5959-;;;; -------------------------------------------------------------------------
  8.5960-;;;; launch-program - semi-portably spawn asynchronous subprocesses
  8.5961-
  8.5962-(uiop/package:define-package :uiop/launch-program
  8.5963-  (:use :uiop/common-lisp :uiop/package :uiop/utility
  8.5964-   :uiop/pathname :uiop/os :uiop/filesystem :uiop/stream
  8.5965-   :uiop/version)
  8.5966-  (:export
  8.5967-   ;;; Escaping the command invocation madness
  8.5968-   #:easy-sh-character-p #:escape-sh-token #:escape-sh-command
  8.5969-   #:escape-windows-token #:escape-windows-command
  8.5970-   #:escape-shell-token #:escape-shell-command
  8.5971-   #:escape-token #:escape-command
  8.5972-
  8.5973-   ;;; launch-program
  8.5974-   #:launch-program
  8.5975-   #:close-streams #:process-alive-p #:terminate-process #:wait-process
  8.5976-   #:process-info
  8.5977-   #:process-info-error-output #:process-info-input #:process-info-output #:process-info-pid))
  8.5978-(in-package :uiop/launch-program)
  8.5979-
  8.5980-;;;; ----- Escaping strings for the shell -----
  8.5981-(with-upgradability ()
  8.5982-  (defun requires-escaping-p (token &key good-chars bad-chars)
  8.5983-    "Does this token require escaping, given the specification of
  8.5984-either good chars that don't need escaping or bad chars that do need escaping,
  8.5985-as either a recognizing function or a sequence of characters."
  8.5986-    (some
  8.5987-     (cond
  8.5988-       ((and good-chars bad-chars)
  8.5989-        (parameter-error "~S: only one of good-chars and bad-chars can be provided"
  8.5990-                         'requires-escaping-p))
  8.5991-       ((typep good-chars 'function)
  8.5992-        (complement good-chars))
  8.5993-       ((typep bad-chars 'function)
  8.5994-        bad-chars)
  8.5995-       ((and good-chars (typep good-chars 'sequence))
  8.5996-        #'(lambda (c) (not (find c good-chars))))
  8.5997-       ((and bad-chars (typep bad-chars 'sequence))
  8.5998-        #'(lambda (c) (find c bad-chars)))
  8.5999-       (t (parameter-error "~S: no good-char criterion" 'requires-escaping-p)))
  8.6000-     token))
  8.6001-
  8.6002-  (defun escape-token (token &key stream quote good-chars bad-chars escaper)
  8.6003-    "Call the ESCAPER function on TOKEN string if it needs escaping as per
  8.6004-REQUIRES-ESCAPING-P using GOOD-CHARS and BAD-CHARS, otherwise output TOKEN,
  8.6005-using STREAM as output (or returning result as a string if NIL)"
  8.6006-    (if (requires-escaping-p token :good-chars good-chars :bad-chars bad-chars)
  8.6007-        (with-output (stream)
  8.6008-          (apply escaper token stream (when quote `(:quote ,quote))))
  8.6009-        (output-string token stream)))
  8.6010-
  8.6011-  (defun escape-windows-token-within-double-quotes (x &optional s)
  8.6012-    "Escape a string token X within double-quotes
  8.6013-for use within a MS Windows command-line, outputing to S."
  8.6014-    (labels ((issue (c) (princ c s))
  8.6015-             (issue-backslash (n) (loop :repeat n :do (issue #\\))))
  8.6016-      (loop
  8.6017-        :initially (issue #\") :finally (issue #\")
  8.6018-        :with l = (length x) :with i = 0
  8.6019-        :for i+1 = (1+ i) :while (< i l) :do
  8.6020-          (case (char x i)
  8.6021-            ((#\") (issue-backslash 1) (issue #\") (setf i i+1))
  8.6022-            ((#\\)
  8.6023-             (let* ((j (and (< i+1 l) (position-if-not
  8.6024-                                       #'(lambda (c) (eql c #\\)) x :start i+1)))
  8.6025-                    (n (- (or j l) i)))
  8.6026-               (cond
  8.6027-                 ((null j)
  8.6028-                  (issue-backslash (* 2 n)) (setf i l))
  8.6029-                 ((and (< j l) (eql (char x j) #\"))
  8.6030-                  (issue-backslash (1+ (* 2 n))) (issue #\") (setf i (1+ j)))
  8.6031-                 (t
  8.6032-                  (issue-backslash n) (setf i j)))))
  8.6033-            (otherwise
  8.6034-             (issue (char x i)) (setf i i+1))))))
  8.6035-
  8.6036-  (defun easy-windows-character-p (x)
  8.6037-    "Is X an \"easy\" character that does not require quoting by the shell?"
  8.6038-    (or (alphanumericp x) (find x "+-_.,@:/=")))
  8.6039-
  8.6040-  (defun escape-windows-token (token &optional s)
  8.6041-    "Escape a string TOKEN within double-quotes if needed
  8.6042-for use within a MS Windows command-line, outputing to S."
  8.6043-    (escape-token token :stream s :good-chars #'easy-windows-character-p :quote nil
  8.6044-                        :escaper 'escape-windows-token-within-double-quotes))
  8.6045-
  8.6046-  (defun escape-sh-token-within-double-quotes (x s &key (quote t))
  8.6047-    "Escape a string TOKEN within double-quotes
  8.6048-for use within a POSIX Bourne shell, outputing to S;
  8.6049-omit the outer double-quotes if key argument :QUOTE is NIL"
  8.6050-    (when quote (princ #\" s))
  8.6051-    (loop :for c :across x :do
  8.6052-      (when (find c "$`\\\"") (princ #\\ s))
  8.6053-      (princ c s))
  8.6054-    (when quote (princ #\" s)))
  8.6055-
  8.6056-  (defun easy-sh-character-p (x)
  8.6057-    "Is X an \"easy\" character that does not require quoting by the shell?"
  8.6058-    (or (alphanumericp x) (find x "+-_.,%@:/=")))
  8.6059-
  8.6060-  (defun escape-sh-token (token &optional s)
  8.6061-    "Escape a string TOKEN within double-quotes if needed
  8.6062-for use within a POSIX Bourne shell, outputing to S."
  8.6063-    (escape-token token :stream s :quote #\" :good-chars #'easy-sh-character-p
  8.6064-                        :escaper 'escape-sh-token-within-double-quotes))
  8.6065-
  8.6066-  (defun escape-shell-token (token &optional s)
  8.6067-    "Escape a token for the current operating system shell"
  8.6068-    (os-cond
  8.6069-      ((os-unix-p) (escape-sh-token token s))
  8.6070-      ((os-windows-p) (escape-windows-token token s))))
  8.6071-
  8.6072-  (defun escape-command (command &optional s
  8.6073-                                  (escaper 'escape-shell-token))
  8.6074-    "Given a COMMAND as a list of tokens, return a string of the
  8.6075-spaced, escaped tokens, using ESCAPER to escape."
  8.6076-    (etypecase command
  8.6077-      (string (output-string command s))
  8.6078-      (list (with-output (s)
  8.6079-              (loop :for first = t :then nil :for token :in command :do
  8.6080-                (unless first (princ #\space s))
  8.6081-                (funcall escaper token s))))))
  8.6082-
  8.6083-  (defun escape-windows-command (command &optional s)
  8.6084-    "Escape a list of command-line arguments into a string suitable for parsing
  8.6085-by CommandLineToArgv in MS Windows"
  8.6086-    ;; http://msdn.microsoft.com/en-us/library/bb776391(v=vs.85).aspx
  8.6087-    ;; http://msdn.microsoft.com/en-us/library/17w5ykft(v=vs.85).aspx
  8.6088-    (escape-command command s 'escape-windows-token))
  8.6089-
  8.6090-  (defun escape-sh-command (command &optional s)
  8.6091-    "Escape a list of command-line arguments into a string suitable for parsing
  8.6092-by /bin/sh in POSIX"
  8.6093-    (escape-command command s 'escape-sh-token))
  8.6094-
  8.6095-  (defun escape-shell-command (command &optional stream)
  8.6096-    "Escape a command for the current operating system's shell"
  8.6097-    (escape-command command stream 'escape-shell-token)))
  8.6098-
  8.6099-
  8.6100-(with-upgradability ()
  8.6101-  ;;; Internal helpers for run-program
  8.6102-  (defun %normalize-io-specifier (specifier &optional role)
  8.6103-    "Normalizes a portable I/O specifier for LAUNCH-PROGRAM into an implementation-dependent
  8.6104-argument to pass to the internal RUN-PROGRAM"
  8.6105-    (declare (ignorable role))
  8.6106-    (typecase specifier
  8.6107-      (null (or #+(or allegro lispworks) (null-device-pathname)))
  8.6108-      (string (parse-native-namestring specifier))
  8.6109-      (pathname specifier)
  8.6110-      (stream specifier)
  8.6111-      ((eql :stream) :stream)
  8.6112-      ((eql :interactive)
  8.6113-       #+(or allegro lispworks) nil
  8.6114-       #+clisp :terminal
  8.6115-       #+(or abcl clasp clozure cmucl ecl mkcl sbcl scl) t
  8.6116-       #-(or abcl clasp clozure cmucl ecl mkcl sbcl scl allegro lispworks clisp)
  8.6117-       (not-implemented-error :interactive-output
  8.6118-                              "On this lisp implementation, cannot interpret ~a value of ~a"
  8.6119-                              specifier role))
  8.6120-      ((eql :output)
  8.6121-       (cond ((eq role :error-output)
  8.6122-              #+(or abcl allegro clasp clozure cmucl ecl lispworks mkcl sbcl scl)
  8.6123-              :output
  8.6124-              #-(or abcl allegro clasp clozure cmucl ecl lispworks mkcl sbcl scl)
  8.6125-              (not-implemented-error :error-output-redirect
  8.6126-                                     "Can't send ~a to ~a on this lisp implementation."
  8.6127-                                     role specifier))
  8.6128-             (t (parameter-error "~S IO specifier invalid for ~S" specifier role))))
  8.6129-      ((eql t)
  8.6130-       #+ (or lispworks abcl)
  8.6131-       (not-implemented-error :interactive-output
  8.6132-                              "On this lisp implementation, cannot interpret ~a value of ~a"
  8.6133-                              specifier role)
  8.6134-       #- (or lispworks abcl)
  8.6135-       (cond ((eq role :error-output) *error-output*)
  8.6136-             ((eq role :output) #+lispworks *terminal-io* #-lispworks *standard-output*)
  8.6137-             ((eq role :input) *standard-input*)))
  8.6138-      (otherwise
  8.6139-       (parameter-error "Incorrect I/O specifier ~S for ~S"
  8.6140-                        specifier role))))
  8.6141-
  8.6142-  (defun %interactivep (input output error-output)
  8.6143-    (member :interactive (list input output error-output)))
  8.6144-
  8.6145-  (defun %signal-to-exit-code (signum)
  8.6146-    (+ 128 signum))
  8.6147-
  8.6148-  (defun %code-to-status (exit-code signal-code)
  8.6149-    (cond ((null exit-code) :running)
  8.6150-          ((null signal-code) (values :exited exit-code))
  8.6151-          (t (values :signaled signal-code))))
  8.6152-
  8.6153-  #+mkcl
  8.6154-  (defun %mkcl-signal-to-number (signal)
  8.6155-    (require :mk-unix)
  8.6156-    (symbol-value (find-symbol signal :mk-unix)))
  8.6157-
  8.6158-  (defclass process-info ()
  8.6159-    (;; The process field is highly platform-, implementation-, and
  8.6160-     ;; even version-dependent.
  8.6161-     ;; Prior to LispWorks 7, the only information that
  8.6162-     ;; `sys:run-shell-command` with `:wait nil` was certain to return
  8.6163-     ;; is a PID (e.g. when all streams are nil), hence we stored it
  8.6164-     ;; and used `sys:pid-exit-status` to obtain an exit status
  8.6165-     ;; later. That is still what we do.
  8.6166-     ;; From LispWorks 7 on, if `sys:run-shell-command` does not
  8.6167-     ;; return a proper stream, we are instead given a dummy stream.
  8.6168-     ;; We can thus always store a stream and use
  8.6169-     ;; `sys:pipe-exit-status` to obtain an exit status later.
  8.6170-     ;; The advantage of dealing with streams instead of PID is the
  8.6171-     ;; availability of functions like `sys:pipe-kill-process`.
  8.6172-     (process :initform nil)
  8.6173-     (input-stream :initform nil)
  8.6174-     (output-stream :initform nil)
  8.6175-     (bidir-stream :initform nil)
  8.6176-     (error-output-stream :initform nil)
  8.6177-     ;; For backward-compatibility, to maintain the property (zerop
  8.6178-     ;; exit-code) <-> success, an exit in response to a signal is
  8.6179-     ;; encoded as 128+signum.
  8.6180-     (exit-code :initform nil)
  8.6181-     ;; If the platform allows it, distinguish exiting with a code
  8.6182-     ;; >128 from exiting in response to a signal by setting this code
  8.6183-     (signal-code :initform nil))
  8.6184-    (:documentation "This class should be treated as opaque by programmers, except for the
  8.6185-exported PROCESS-INFO-* functions.  It should never be directly instantiated by
  8.6186-MAKE-INSTANCE. Primarily, it is being made available to enable type-checking."))
  8.6187-
  8.6188-;;;---------------------------------------------------------------------------
  8.6189-;;; The following two helper functions take care of handling the IF-EXISTS and
  8.6190-;;; IF-DOES-NOT-EXIST arguments for RUN-PROGRAM. In particular, they process the
  8.6191-;;; :ERROR, :APPEND, and :SUPERSEDE arguments *here*, allowing the master
  8.6192-;;; function to treat input and output files unconditionally for reading and
  8.6193-;;; writing.
  8.6194-;;;---------------------------------------------------------------------------
  8.6195-
  8.6196-  (defun %handle-if-exists (file if-exists)
  8.6197-    (when (or (stringp file) (pathnamep file))
  8.6198-      (ecase if-exists
  8.6199-        ((:append :supersede :error)
  8.6200-         (with-open-file (dummy file :direction :output :if-exists if-exists)
  8.6201-           (declare (ignorable dummy)))))))
  8.6202-
  8.6203-  (defun %handle-if-does-not-exist (file if-does-not-exist)
  8.6204-    (when (or (stringp file) (pathnamep file))
  8.6205-      (ecase if-does-not-exist
  8.6206-        ((:create :error)
  8.6207-         (with-open-file (dummy file :direction :probe
  8.6208-                                :if-does-not-exist if-does-not-exist)
  8.6209-           (declare (ignorable dummy)))))))
  8.6210-
  8.6211-  (defun process-info-error-output (process-info)
  8.6212-    (slot-value process-info 'error-output-stream))
  8.6213-  (defun process-info-input (process-info)
  8.6214-    (or (slot-value process-info 'bidir-stream)
  8.6215-        (slot-value process-info 'input-stream)))
  8.6216-  (defun process-info-output (process-info)
  8.6217-    (or (slot-value process-info 'bidir-stream)
  8.6218-        (slot-value process-info 'output-stream)))
  8.6219-
  8.6220-  (defun process-info-pid (process-info)
  8.6221-    (let ((process (slot-value process-info 'process)))
  8.6222-      (declare (ignorable process))
  8.6223-      #+abcl (symbol-call :sys :process-pid process)
  8.6224-      #+allegro process
  8.6225-      #+clasp (if (find-symbol* '#:external-process-pid :ext nil)
  8.6226-                  (symbol-call :ext '#:external-process-pid process)
  8.6227-                  (not-implemented-error 'process-info-pid))
  8.6228-      #+clozure (ccl:external-process-id process)
  8.6229-      #+ecl (ext:external-process-pid process)
  8.6230-      #+(or cmucl scl) (ext:process-pid process)
  8.6231-      #+lispworks7+ (sys:pipe-pid process)
  8.6232-      #+(and lispworks (not lispworks7+)) process
  8.6233-      #+mkcl (mkcl:process-id process)
  8.6234-      #+sbcl (sb-ext:process-pid process)
  8.6235-      #-(or abcl allegro clasp clozure cmucl ecl mkcl lispworks sbcl scl)
  8.6236-      (not-implemented-error 'process-info-pid)))
  8.6237-
  8.6238-  (defun %process-status (process-info)
  8.6239-    (if-let (exit-code (slot-value process-info 'exit-code))
  8.6240-      (return-from %process-status
  8.6241-        (if-let (signal-code (slot-value process-info 'signal-code))
  8.6242-          (values :signaled signal-code)
  8.6243-          (values :exited exit-code))))
  8.6244-    #-(or allegro clasp clozure cmucl ecl lispworks mkcl sbcl scl)
  8.6245-    (not-implemented-error '%process-status)
  8.6246-    (if-let (process (slot-value process-info 'process))
  8.6247-      (multiple-value-bind (status code)
  8.6248-          (progn
  8.6249-            #+allegro (multiple-value-bind (exit-code pid signal-code)
  8.6250-                          (sys:reap-os-subprocess :pid process :wait nil)
  8.6251-                        (assert pid)
  8.6252-                        (%code-to-status exit-code signal-code))
  8.6253-            #+clasp (if (find-symbol* '#:external-process-status :ext nil)
  8.6254-                        (symbol-call :ext '#:external-process-status process)
  8.6255-                        (not-implemented-error '%process-status))
  8.6256-            #+clozure (ccl:external-process-status process)
  8.6257-            #+(or cmucl scl) (let ((status (ext:process-status process)))
  8.6258-                               (if (member status '(:exited :signaled))
  8.6259-                                   ;; Calling ext:process-exit-code on
  8.6260-                                   ;; processes that are still alive
  8.6261-                                   ;; yields an undefined result
  8.6262-                                   (values status (ext:process-exit-code process))
  8.6263-                                   status))
  8.6264-            #+ecl (ext:external-process-status process)
  8.6265-            #+lispworks
  8.6266-            ;; a signal is only returned on LispWorks 7+
  8.6267-            (multiple-value-bind (exit-code signal-code)
  8.6268-                (symbol-call :sys
  8.6269-                             #+lispworks7+ :pipe-exit-status
  8.6270-                             #-lispworks7+ :pid-exit-status
  8.6271-                             process :wait nil)
  8.6272-              (%code-to-status exit-code signal-code))
  8.6273-            #+mkcl (let ((status (mk-ext:process-status process)))
  8.6274-                     (if (eq status :exited)
  8.6275-                         ;; Only call mk-ext:process-exit-code when
  8.6276-                         ;; necessary since it leads to another waitpid()
  8.6277-                         (let ((code (mk-ext:process-exit-code process)))
  8.6278-                           (if (stringp code)
  8.6279-                               (values :signaled (%mkcl-signal-to-number code))
  8.6280-                               (values :exited code)))
  8.6281-                         status))
  8.6282-            #+sbcl (let ((status (sb-ext:process-status process)))
  8.6283-                     (if (eq status :running)
  8.6284-                         :running
  8.6285-                         ;; sb-ext:process-exit-code can also be
  8.6286-                         ;; called for stopped processes to determine
  8.6287-                         ;; the signal that stopped them
  8.6288-                         (values status (sb-ext:process-exit-code process)))))
  8.6289-        (case status
  8.6290-          (:exited (setf (slot-value process-info 'exit-code) code))
  8.6291-          (:signaled (let ((%code (%signal-to-exit-code code)))
  8.6292-                       (setf (slot-value process-info 'exit-code) %code
  8.6293-                             (slot-value process-info 'signal-code) code))))
  8.6294-        (if code
  8.6295-            (values status code)
  8.6296-            status))))
  8.6297-
  8.6298-  (defun process-alive-p (process-info)
  8.6299-    "Check if a process has yet to exit."
  8.6300-    (unless (slot-value process-info 'exit-code)
  8.6301-      #+abcl (sys:process-alive-p (slot-value process-info 'process))
  8.6302-      #+(or cmucl scl) (ext:process-alive-p (slot-value process-info 'process))
  8.6303-      #+sbcl (sb-ext:process-alive-p (slot-value process-info 'process))
  8.6304-      #-(or abcl cmucl sbcl scl) (find (%process-status process-info)
  8.6305-                                       '(:running :stopped :continued :resumed))))
  8.6306-
  8.6307-  (defun wait-process (process-info)
  8.6308-    "Wait for the process to terminate, if it is still running.
  8.6309-Otherwise, return immediately. An exit code (a number) will be
  8.6310-returned, with 0 indicating success, and anything else indicating
  8.6311-failure. If the process exits after receiving a signal, the exit code
  8.6312-will be the sum of 128 and the (positive) numeric signal code. A second
  8.6313-value may be returned in this case: the numeric signal code itself.
  8.6314-Any asynchronously spawned process requires this function to be run
  8.6315-before it is garbage-collected in order to free up resources that
  8.6316-might otherwise be irrevocably lost."
  8.6317-    (if-let (exit-code (slot-value process-info 'exit-code))
  8.6318-      (if-let (signal-code (slot-value process-info 'signal-code))
  8.6319-        (values exit-code signal-code)
  8.6320-        exit-code)
  8.6321-      (let ((process (slot-value process-info 'process)))
  8.6322-        #-(or abcl allegro clasp clozure cmucl ecl lispworks mkcl sbcl scl)
  8.6323-        (not-implemented-error 'wait-process)
  8.6324-        (when process
  8.6325-          ;; 1- wait
  8.6326-          #+clozure (ccl::external-process-wait process)
  8.6327-          #+(or cmucl scl) (ext:process-wait process)
  8.6328-          #+sbcl (sb-ext:process-wait process)
  8.6329-          ;; 2- extract result
  8.6330-          (multiple-value-bind (exit-code signal-code)
  8.6331-              (progn
  8.6332-                #+abcl (sys:process-wait process)
  8.6333-                #+allegro (multiple-value-bind (exit-code pid signal)
  8.6334-                              (sys:reap-os-subprocess :pid process :wait t)
  8.6335-                            (assert pid)
  8.6336-                            (values exit-code signal))
  8.6337-                #+clasp (if (find-symbol* '#:external-process-wait :ext nil)
  8.6338-                            (multiple-value-bind (status code)
  8.6339-                                (symbol-call :ext '#:external-process-wait process t)
  8.6340-                              (if (eq status :signaled)
  8.6341-                                  (values nil code)
  8.6342-                                  code))
  8.6343-                            (not-implemented-error 'wait-process))
  8.6344-                #+clozure (multiple-value-bind (status code)
  8.6345-                              (ccl:external-process-status process)
  8.6346-                            (if (eq status :signaled)
  8.6347-                                (values nil code)
  8.6348-                                code))
  8.6349-                #+(or cmucl scl) (let ((status (ext:process-status process))
  8.6350-                                       (code (ext:process-exit-code process)))
  8.6351-                                   (if (eq status :signaled)
  8.6352-                                       (values nil code)
  8.6353-                                       code))
  8.6354-                #+ecl (multiple-value-bind (status code)
  8.6355-                          (ext:external-process-wait process t)
  8.6356-                        (if (eq status :signaled)
  8.6357-                            (values nil code)
  8.6358-                            code))
  8.6359-                #+lispworks (symbol-call :sys
  8.6360-                                         #+lispworks7+ :pipe-exit-status
  8.6361-                                         #-lispworks7+ :pid-exit-status
  8.6362-                                         process :wait t)
  8.6363-                #+mkcl (let ((code (mkcl:join-process process)))
  8.6364-                         (if (stringp code)
  8.6365-                             (values nil (%mkcl-signal-to-number code))
  8.6366-                             code))
  8.6367-                #+sbcl (let ((status (sb-ext:process-status process))
  8.6368-                             (code (sb-ext:process-exit-code process)))
  8.6369-                         (if (eq status :signaled)
  8.6370-                             (values nil code)
  8.6371-                             code)))
  8.6372-            (if signal-code
  8.6373-                (let ((%exit-code (%signal-to-exit-code signal-code)))
  8.6374-                  (setf (slot-value process-info 'exit-code) %exit-code
  8.6375-                        (slot-value process-info 'signal-code) signal-code)
  8.6376-                  (values %exit-code signal-code))
  8.6377-                (progn (setf (slot-value process-info 'exit-code) exit-code)
  8.6378-                       exit-code)))))))
  8.6379-
  8.6380-  ;; WARNING: For signals other than SIGTERM and SIGKILL this may not
  8.6381-  ;; do what you expect it to. Sending SIGSTOP to a process spawned
  8.6382-  ;; via LAUNCH-PROGRAM, e.g., will stop the shell /bin/sh that is used
  8.6383-  ;; to run the command (via `sh -c command`) but not the actual
  8.6384-  ;; command.
  8.6385-  #+os-unix
  8.6386-  (defun %posix-send-signal (process-info signal)
  8.6387-    #+allegro (excl.osi:kill (slot-value process-info 'process) signal)
  8.6388-    #+clozure (ccl:signal-external-process (slot-value process-info 'process)
  8.6389-                                           signal :error-if-exited nil)
  8.6390-    #+(or cmucl scl) (ext:process-kill (slot-value process-info 'process) signal)
  8.6391-    #+sbcl (sb-ext:process-kill (slot-value process-info 'process) signal)
  8.6392-    #-(or allegro clozure cmucl sbcl scl)
  8.6393-    (if-let (pid (process-info-pid process-info))
  8.6394-      (symbol-call :uiop :run-program
  8.6395-                   (format nil "kill -~a ~a" signal pid) :ignore-error-status t)))
  8.6396-
  8.6397-  ;;; this function never gets called on Windows, but the compiler cannot tell
  8.6398-  ;;; that. [2016/09/25:rpg]
  8.6399-  #+os-windows
  8.6400-  (defun %posix-send-signal (process-info signal)
  8.6401-    (declare (ignore process-info signal))
  8.6402-    (values))
  8.6403-
  8.6404-  (defun terminate-process (process-info &key urgent)
  8.6405-    "Cause the process to exit. To that end, the process may or may
  8.6406-not be sent a signal, which it will find harder (or even impossible)
  8.6407-to ignore if URGENT is T. On some platforms, it may also be subject to
  8.6408-race conditions."
  8.6409-    (declare (ignorable urgent))
  8.6410-    #+abcl (sys:process-kill (slot-value process-info 'process))
  8.6411-    ;; On ECL, this will only work on versions later than 2016-09-06,
  8.6412-    ;; but we still want to compile on earlier versions, so we use symbol-call
  8.6413-    #+(or clasp ecl) (symbol-call :ext :terminate-process (slot-value process-info 'process) urgent)
  8.6414-    #+lispworks7+ (sys:pipe-kill-process (slot-value process-info 'process))
  8.6415-    #+mkcl (mk-ext:terminate-process (slot-value process-info 'process)
  8.6416-                                     :force urgent)
  8.6417-    #-(or abcl clasp ecl lispworks7+ mkcl)
  8.6418-    (os-cond
  8.6419-     ((os-unix-p) (%posix-send-signal process-info (if urgent 9 15)))
  8.6420-     ((os-windows-p) (if-let (pid (process-info-pid process-info))
  8.6421-                       (symbol-call :uiop :run-program
  8.6422-                                    (format nil "taskkill ~:[~;/f ~]/pid ~a" urgent pid)
  8.6423-                                    :ignore-error-status t)))
  8.6424-     (t (not-implemented-error 'terminate-process))))
  8.6425-
  8.6426-  (defun close-streams (process-info)
  8.6427-    "Close any stream that the process might own. Needs to be run
  8.6428-whenever streams were requested by passing :stream to :input, :output,
  8.6429-or :error-output."
  8.6430-    (dolist (stream
  8.6431-              (cons (slot-value process-info 'error-output-stream)
  8.6432-                    (if-let (bidir-stream (slot-value process-info 'bidir-stream))
  8.6433-                      (list bidir-stream)
  8.6434-                      (list (slot-value process-info 'input-stream)
  8.6435-                            (slot-value process-info 'output-stream)))))
  8.6436-      (when stream (close stream))))
  8.6437-
  8.6438-  (defun launch-program (command &rest keys
  8.6439-                         &key
  8.6440-                           input (if-input-does-not-exist :error)
  8.6441-                           output (if-output-exists :supersede)
  8.6442-                           error-output (if-error-output-exists :supersede)
  8.6443-                           (element-type #-clozure *default-stream-element-type*
  8.6444-                                         #+clozure 'character)
  8.6445-                           (external-format *utf-8-external-format*)
  8.6446-                           directory
  8.6447-                           #+allegro separate-streams
  8.6448-                           &allow-other-keys)
  8.6449-    "Launch program specified by COMMAND,
  8.6450-either a list of strings specifying a program and list of arguments,
  8.6451-or a string specifying a shell command (/bin/sh on Unix, CMD.EXE on
  8.6452-Windows) _asynchronously_.
  8.6453-
  8.6454-If OUTPUT is a pathname, a string designating a pathname, or NIL (the
  8.6455-default) designating the null device, the file at that path is used as
  8.6456-output.
  8.6457-If it's :INTERACTIVE, output is inherited from the current process;
  8.6458-beware that this may be different from your *STANDARD-OUTPUT*, and
  8.6459-under SLIME will be on your *inferior-lisp* buffer.  If it's T, output
  8.6460-goes to your current *STANDARD-OUTPUT* stream.  If it's :STREAM, a new
  8.6461-stream will be made available that can be accessed via
  8.6462-PROCESS-INFO-OUTPUT and read from. Otherwise, OUTPUT should be a value
  8.6463-that the underlying lisp implementation knows how to handle.
  8.6464-
  8.6465-IF-OUTPUT-EXISTS, which is only meaningful if OUTPUT is a string or a
  8.6466-pathname, can take the values :ERROR, :APPEND, and :SUPERSEDE (the
  8.6467-default). The meaning of these values and their effect on the case
  8.6468-where OUTPUT does not exist, is analogous to the IF-EXISTS parameter
  8.6469-to OPEN with :DIRECTION :OUTPUT.
  8.6470-
  8.6471-ERROR-OUTPUT is similar to OUTPUT. T designates the *ERROR-OUTPUT*,
  8.6472-:OUTPUT means redirecting the error output to the output stream,
  8.6473-and :STREAM causes a stream to be made available via
  8.6474-PROCESS-INFO-ERROR-OUTPUT.
  8.6475-
  8.6476-IF-ERROR-OUTPUT-EXISTS is similar to IF-OUTPUT-EXIST, except that it
  8.6477-affects ERROR-OUTPUT rather than OUTPUT.
  8.6478-
  8.6479-INPUT is similar to OUTPUT, except that T designates the
  8.6480-*STANDARD-INPUT* and a stream requested through the :STREAM keyword
  8.6481-would be available through PROCESS-INFO-INPUT.
  8.6482-
  8.6483-IF-INPUT-DOES-NOT-EXIST, which is only meaningful if INPUT is a string
  8.6484-or a pathname, can take the values :CREATE and :ERROR (the
  8.6485-default). The meaning of these values is analogous to the
  8.6486-IF-DOES-NOT-EXIST parameter to OPEN with :DIRECTION :INPUT.
  8.6487-
  8.6488-ELEMENT-TYPE and EXTERNAL-FORMAT are passed on to your Lisp
  8.6489-implementation, when applicable, for creation of the output stream.
  8.6490-
  8.6491-LAUNCH-PROGRAM returns a PROCESS-INFO object.
  8.6492-
  8.6493-LAUNCH-PROGRAM currently does not smooth over all the differences between
  8.6494-implementations. Of particular note is when streams are provided for OUTPUT or
  8.6495-ERROR-OUTPUT. Some implementations don't support this at all, some support only
  8.6496-certain subclasses of streams, and some support any arbitrary
  8.6497-stream. Additionally, the implementations that support streams may have
  8.6498-differing behavior on how those streams are filled with data. If data is not
  8.6499-periodically read from the child process and sent to the stream, the child
  8.6500-could block because its output buffers are full."
  8.6501-    #-(or abcl allegro clasp clozure cmucl ecl (and lispworks os-unix) mkcl sbcl scl)
  8.6502-    (progn command keys input output error-output directory element-type external-format
  8.6503-           if-input-does-not-exist if-output-exists if-error-output-exists ;; ignore
  8.6504-           (not-implemented-error 'launch-program))
  8.6505-    #+allegro
  8.6506-    (when (some #'(lambda (stream)
  8.6507-                    (and (streamp stream)
  8.6508-                         (not (file-stream-p stream))))
  8.6509-                (list input output error-output))
  8.6510-      (parameter-error "~S: Streams passed as I/O parameters need to be file streams on this lisp"
  8.6511-                       'launch-program))
  8.6512-    #+(or abcl clisp lispworks)
  8.6513-    (when (some #'streamp (list input output error-output))
  8.6514-      (parameter-error "~S: I/O parameters cannot be foreign streams on this lisp"
  8.6515-                       'launch-program))
  8.6516-    #+clisp
  8.6517-    (unless (eq error-output :interactive)
  8.6518-      (parameter-error "~S: The only admissible value for ~S is ~S on this lisp"
  8.6519-                       'launch-program :error-output :interactive))
  8.6520-    #+(or clasp ecl)
  8.6521-    (when (and #+ecl (version< (lisp-implementation-version) "20.4.24")
  8.6522-               (some #'(lambda (stream)
  8.6523-                         (and (streamp stream)
  8.6524-                              (not (file-or-synonym-stream-p stream))))
  8.6525-                     (list input output error-output)))
  8.6526-      (parameter-error "~S: Streams passed as I/O parameters need to be (synonymous with) file streams on this lisp"
  8.6527-                       'launch-program))
  8.6528-    #+(or abcl allegro clasp clozure cmucl ecl (and lispworks os-unix) mkcl sbcl scl)
  8.6529-    (nest
  8.6530-     (progn ;; see comments for these functions
  8.6531-       (%handle-if-does-not-exist input if-input-does-not-exist)
  8.6532-       (%handle-if-exists output if-output-exists)
  8.6533-       (%handle-if-exists error-output if-error-output-exists))
  8.6534-     #+(or clasp ecl) (let ((*standard-input* *stdin*)
  8.6535-                 (*standard-output* *stdout*)
  8.6536-                 (*error-output* *stderr*)))
  8.6537-     (let ((process-info (make-instance 'process-info))
  8.6538-           (input (%normalize-io-specifier input :input))
  8.6539-           (output (%normalize-io-specifier output :output))
  8.6540-           (error-output (%normalize-io-specifier error-output :error-output))
  8.6541-           #+(and allegro os-windows) (interactive (%interactivep input output error-output))
  8.6542-           (command
  8.6543-            (etypecase command
  8.6544-              #+os-unix (string `("/bin/sh" "-c" ,command))
  8.6545-              #+os-unix (list command)
  8.6546-              #+os-windows
  8.6547-              (string
  8.6548-               ;; NB: On other Windows implementations, this is utterly bogus
  8.6549-               ;; except in the most trivial cases where no quoting is needed.
  8.6550-               ;; Use at your own risk.
  8.6551-               #-(or allegro clasp clisp clozure ecl)
  8.6552-               (nest
  8.6553-                #+(or clasp ecl sbcl) (unless (find-symbol* :escape-arguments #+(or clasp ecl) :ext #+sbcl :sb-impl nil))
  8.6554-                (parameter-error "~S doesn't support string commands on Windows on this Lisp"
  8.6555-                                 'launch-program command))
  8.6556-               ;; NB: We add cmd /c here. Behavior without going through cmd is not well specified
  8.6557-               ;; when the command contains spaces or special characters:
  8.6558-               ;; IIUC, the system will use space as a separator,
  8.6559-               ;; but the C++ argv-decoding libraries won't, and
  8.6560-               ;; you're supposed to use an extra argument to CreateProcess to bridge the gap,
  8.6561-               ;; yet neither allegro nor clisp provide access to that argument.
  8.6562-               #+(or allegro clisp) (strcat "cmd /c " command)
  8.6563-               ;; On ClozureCL for Windows, we assume you are using
  8.6564-               ;; r15398 or later in 1.9 or later,
  8.6565-               ;; so that bug 858 is fixed http://trac.clozure.com/ccl/ticket/858
  8.6566-               ;; On ECL, commit 2040629 https://gitlab.com/embeddable-common-lisp/ecl/issues/304
  8.6567-               ;; On SBCL, we assume the patch from fcae0fd (to be part of SBCL 1.3.13)
  8.6568-               #+(or clasp clozure ecl sbcl) (cons "cmd" (strcat "/c " command)))
  8.6569-              #+os-windows
  8.6570-              (list
  8.6571-               #+allegro (escape-windows-command command)
  8.6572-               #-allegro command)))))
  8.6573-     #+(or abcl (and allegro os-unix) clasp clozure cmucl ecl mkcl sbcl)
  8.6574-     (let ((program (car command))
  8.6575-           #-allegro (arguments (cdr command))))
  8.6576-     #+(and (or clasp ecl sbcl) os-windows)
  8.6577-     (multiple-value-bind (arguments escape-arguments)
  8.6578-         (if (listp arguments)
  8.6579-             (values arguments t)
  8.6580-             (values (list arguments) nil)))
  8.6581-     #-(or allegro mkcl sbcl) (with-current-directory (directory))
  8.6582-     (multiple-value-bind
  8.6583-       #+(or abcl clozure cmucl sbcl scl) (process)
  8.6584-       #+allegro (in-or-io out-or-err err-or-pid pid-or-nil)
  8.6585-       #+(or clasp ecl) (stream code process)
  8.6586-       #+lispworks (io-or-pid err-or-nil #-lispworks7+ pid-or-nil)
  8.6587-       #+mkcl (stream process code)
  8.6588-       #.`(apply
  8.6589-           #+abcl 'sys:run-program
  8.6590-           #+allegro ,@'('excl:run-shell-command
  8.6591-                         #+os-unix (coerce (cons program command) 'vector)
  8.6592-                         #+os-windows command)
  8.6593-           #+clasp (if (find-symbol* '#:run-program :ext nil)
  8.6594-                       (find-symbol* '#:run-program :ext nil)
  8.6595-                       (not-implemented-error 'launch-program))
  8.6596-           #+clozure 'ccl:run-program
  8.6597-           #+(or cmucl ecl scl) 'ext:run-program
  8.6598-           
  8.6599-           #+lispworks ,@'('system:run-shell-command `("/usr/bin/env" ,@command)) ; full path needed
  8.6600-           #+mkcl 'mk-ext:run-program
  8.6601-           #+sbcl 'sb-ext:run-program
  8.6602-           #+(or abcl clasp clozure cmucl ecl mkcl sbcl) ,@'(program arguments)
  8.6603-           #+(and (or clasp ecl sbcl) os-windows) ,@'(:escape-arguments escape-arguments)
  8.6604-           :input input :if-input-does-not-exist :error
  8.6605-           :output output :if-output-exists :append
  8.6606-           ,(or #+(or allegro lispworks) :error-output :error) error-output
  8.6607-           ,(or #+(or allegro lispworks) :if-error-output-exists :if-error-exists) :append
  8.6608-           :wait nil :element-type element-type :external-format external-format
  8.6609-           :allow-other-keys t
  8.6610-           #+allegro ,@`(:directory directory
  8.6611-                         #+os-windows ,@'(:show-window (if interactive nil :hide)))
  8.6612-           #+lispworks ,@'(:save-exit-status t)
  8.6613-           #+mkcl ,@'(:directory (native-namestring directory))
  8.6614-           #-sbcl keys ;; on SBCL, don't pass :directory nil but remove it from the keys
  8.6615-           #+sbcl ,@'(:search t (if directory keys (remove-plist-key :directory keys)))))
  8.6616-     (labels ((prop (key value) (setf (slot-value process-info key) value)))
  8.6617-       #+allegro
  8.6618-       (cond
  8.6619-         (separate-streams
  8.6620-          (prop 'process pid-or-nil)
  8.6621-          (when (eq input :stream) (prop 'input-stream in-or-io))
  8.6622-          (when (eq output :stream) (prop 'output-stream out-or-err))
  8.6623-          (when (eq error-output :stream) (prop 'error-output-stream err-or-pid)))
  8.6624-         (t
  8.6625-          (prop 'process err-or-pid)
  8.6626-          (ecase (+ (if (eq input :stream) 1 0) (if (eq output :stream) 2 0))
  8.6627-            (0)
  8.6628-            (1 (prop 'input-stream in-or-io))
  8.6629-            (2 (prop 'output-stream in-or-io))
  8.6630-            (3 (prop 'bidir-stream in-or-io)))
  8.6631-          (when (eq error-output :stream)
  8.6632-            (prop 'error-output-stream out-or-err))))
  8.6633-       #+(or abcl clozure cmucl sbcl scl)
  8.6634-       (progn
  8.6635-         (prop 'process process)
  8.6636-         (when (eq input :stream)
  8.6637-           (nest
  8.6638-            (prop 'input-stream)
  8.6639-            #+abcl (symbol-call :sys :process-input)
  8.6640-            #+clozure (ccl:external-process-input-stream)
  8.6641-            #+(or cmucl scl) (ext:process-input)
  8.6642-            #+sbcl (sb-ext:process-input)
  8.6643-            process))
  8.6644-         (when (eq output :stream)
  8.6645-           (nest
  8.6646-            (prop 'output-stream)
  8.6647-            #+abcl (symbol-call :sys :process-output)
  8.6648-            #+clozure (ccl:external-process-output-stream)
  8.6649-            #+(or cmucl scl) (ext:process-output)
  8.6650-            #+sbcl (sb-ext:process-output)
  8.6651-            process))
  8.6652-         (when (eq error-output :stream)
  8.6653-           (nest
  8.6654-            (prop 'error-output-stream)
  8.6655-            #+abcl (symbol-call :sys :process-error)
  8.6656-            #+clozure (ccl:external-process-error-stream)
  8.6657-            #+(or cmucl scl) (ext:process-error)
  8.6658-            #+sbcl (sb-ext:process-error)
  8.6659-            process)))
  8.6660-       #+(or clasp ecl mkcl)
  8.6661-       (let ((mode (+ (if (eq input :stream) 1 0) (if (eq output :stream) 2 0))))
  8.6662-         code ;; ignore
  8.6663-         (unless (zerop mode)
  8.6664-           (prop (case mode (1 'input-stream) (2 'output-stream) (3 'bidir-stream)) stream))
  8.6665-         (when (eq error-output :stream)
  8.6666-           (prop 'error-output-stream
  8.6667-                 (if (and #+clasp nil #-clasp t (version< (lisp-implementation-version) "16.0.0"))
  8.6668-                     (symbol-call :ext :external-process-error process)
  8.6669-                     (symbol-call :ext :external-process-error-stream process))))
  8.6670-         (prop 'process process))
  8.6671-       #+lispworks
  8.6672-       ;; See also the comments on the process-info class
  8.6673-       (let ((mode (+ (if (eq input :stream) 1 0) (if (eq output :stream) 2 0))))
  8.6674-         (cond
  8.6675-           ((or (plusp mode) (eq error-output :stream))
  8.6676-            (prop 'process #+lispworks7+ io-or-pid #-lispworks7+ pid-or-nil)
  8.6677-            (when (plusp mode)
  8.6678-              (prop (ecase mode (1 'input-stream) (2 'output-stream) (3 'bidir-stream))
  8.6679-                    io-or-pid))
  8.6680-            (when (eq error-output :stream)
  8.6681-              (prop 'error-output-stream err-or-nil)))
  8.6682-           ;; Prior to Lispworks 7, this returned (pid); now it
  8.6683-           ;; returns (io err pid) of which we keep io.
  8.6684-           (t (prop 'process io-or-pid)))))
  8.6685-     process-info)))
  8.6686-
  8.6687-;;;; -------------------------------------------------------------------------
  8.6688-;;;; run-program initially from xcvb-driver.
  8.6689-
  8.6690-(uiop/package:define-package :uiop/run-program
  8.6691-  (:nicknames :asdf/run-program) ; OBSOLETE. Used by cl-sane, printv.
  8.6692-  (:use :uiop/common-lisp :uiop/package :uiop/utility :uiop/version
  8.6693-   :uiop/pathname :uiop/os :uiop/filesystem :uiop/stream :uiop/launch-program)
  8.6694-  (:export
  8.6695-   #:run-program
  8.6696-   #:slurp-input-stream #:vomit-output-stream
  8.6697-   #:subprocess-error
  8.6698-   #:subprocess-error-code #:subprocess-error-command #:subprocess-error-process)
  8.6699-  (:import-from :uiop/launch-program
  8.6700-   #:%handle-if-does-not-exist #:%handle-if-exists #:%interactivep
  8.6701-   #:input-stream #:output-stream #:error-output-stream))
  8.6702-(in-package :uiop/run-program)
  8.6703-
  8.6704-;;;; Slurping a stream, typically the output of another program
  8.6705-(with-upgradability ()
  8.6706-  (defun call-stream-processor (fun processor stream)
  8.6707-    "Given FUN (typically SLURP-INPUT-STREAM or VOMIT-OUTPUT-STREAM,
  8.6708-a PROCESSOR specification which is either an atom or a list specifying
  8.6709-a processor an keyword arguments, call the specified processor with
  8.6710-the given STREAM as input"
  8.6711-    (if (consp processor)
  8.6712-        (apply fun (first processor) stream (rest processor))
  8.6713-        (funcall fun processor stream)))
  8.6714-
  8.6715-  (defgeneric slurp-input-stream (processor input-stream &key)
  8.6716-    (:documentation
  8.6717-     "SLURP-INPUT-STREAM is a generic function with two positional arguments
  8.6718-PROCESSOR and INPUT-STREAM and additional keyword arguments, that consumes (slurps)
  8.6719-the contents of the INPUT-STREAM and processes them according to a method
  8.6720-specified by PROCESSOR.
  8.6721-
  8.6722-Built-in methods include the following:
  8.6723-* if PROCESSOR is a function, it is called with the INPUT-STREAM as its argument
  8.6724-* if PROCESSOR is a list, its first element should be a function.  It will be applied to a cons of the
  8.6725-  INPUT-STREAM and the rest of the list.  That is (x . y) will be treated as
  8.6726-    \(APPLY x <stream> y\)
  8.6727-* if PROCESSOR is an output-stream, the contents of INPUT-STREAM is copied to the output-stream,
  8.6728-  per copy-stream-to-stream, with appropriate keyword arguments.
  8.6729-* if PROCESSOR is the symbol CL:STRING or the keyword :STRING, then the contents of INPUT-STREAM
  8.6730-  are returned as a string, as per SLURP-STREAM-STRING.
  8.6731-* if PROCESSOR is the keyword :LINES then the INPUT-STREAM will be handled by SLURP-STREAM-LINES.
  8.6732-* if PROCESSOR is the keyword :LINE then the INPUT-STREAM will be handled by SLURP-STREAM-LINE.
  8.6733-* if PROCESSOR is the keyword :FORMS then the INPUT-STREAM will be handled by SLURP-STREAM-FORMS.
  8.6734-* if PROCESSOR is the keyword :FORM then the INPUT-STREAM will be handled by SLURP-STREAM-FORM.
  8.6735-* if PROCESSOR is T, it is treated the same as *standard-output*. If it is NIL, NIL is returned.
  8.6736-
  8.6737-Programmers are encouraged to define their own methods for this generic function."))
  8.6738-
  8.6739-  #-genera
  8.6740-  (defmethod slurp-input-stream ((function function) input-stream &key)
  8.6741-    (funcall function input-stream))
  8.6742-
  8.6743-  (defmethod slurp-input-stream ((list cons) input-stream &key)
  8.6744-    (apply (first list) input-stream (rest list)))
  8.6745-
  8.6746-  #-genera
  8.6747-  (defmethod slurp-input-stream ((output-stream stream) input-stream
  8.6748-                                 &key linewise prefix (element-type 'character) buffer-size)
  8.6749-    (copy-stream-to-stream
  8.6750-     input-stream output-stream
  8.6751-     :linewise linewise :prefix prefix :element-type element-type :buffer-size buffer-size))
  8.6752-
  8.6753-  (defmethod slurp-input-stream ((x (eql 'string)) stream &key stripped)
  8.6754-    (slurp-stream-string stream :stripped stripped))
  8.6755-
  8.6756-  (defmethod slurp-input-stream ((x (eql :string)) stream &key stripped)
  8.6757-    (slurp-stream-string stream :stripped stripped))
  8.6758-
  8.6759-  (defmethod slurp-input-stream ((x (eql :lines)) stream &key count)
  8.6760-    (slurp-stream-lines stream :count count))
  8.6761-
  8.6762-  (defmethod slurp-input-stream ((x (eql :line)) stream &key (at 0))
  8.6763-    (slurp-stream-line stream :at at))
  8.6764-
  8.6765-  (defmethod slurp-input-stream ((x (eql :forms)) stream &key count)
  8.6766-    (slurp-stream-forms stream :count count))
  8.6767-
  8.6768-  (defmethod slurp-input-stream ((x (eql :form)) stream &key (at 0))
  8.6769-    (slurp-stream-form stream :at at))
  8.6770-
  8.6771-  (defmethod slurp-input-stream ((x (eql t)) stream &rest keys &key &allow-other-keys)
  8.6772-    (apply 'slurp-input-stream *standard-output* stream keys))
  8.6773-
  8.6774-  (defmethod slurp-input-stream ((x null) (stream t) &key)
  8.6775-    nil)
  8.6776-
  8.6777-  (defmethod slurp-input-stream ((pathname pathname) input
  8.6778-                                 &key
  8.6779-                                   (element-type *default-stream-element-type*)
  8.6780-                                   (external-format *utf-8-external-format*)
  8.6781-                                   (if-exists :rename-and-delete)
  8.6782-                                   (if-does-not-exist :create)
  8.6783-                                   buffer-size
  8.6784-                                   linewise)
  8.6785-    (with-output-file (output pathname
  8.6786-                              :element-type element-type
  8.6787-                              :external-format external-format
  8.6788-                              :if-exists if-exists
  8.6789-                              :if-does-not-exist if-does-not-exist)
  8.6790-      (copy-stream-to-stream
  8.6791-       input output
  8.6792-       :element-type element-type :buffer-size buffer-size :linewise linewise)))
  8.6793-
  8.6794-  (defmethod slurp-input-stream (x stream
  8.6795-                                 &key linewise prefix (element-type 'character) buffer-size)
  8.6796-    (declare (ignorable stream linewise prefix element-type buffer-size))
  8.6797-    (cond
  8.6798-      #+genera
  8.6799-      ((functionp x) (funcall x stream))
  8.6800-      #+genera
  8.6801-      ((output-stream-p x)
  8.6802-       (copy-stream-to-stream
  8.6803-        stream x
  8.6804-        :linewise linewise :prefix prefix :element-type element-type :buffer-size buffer-size))
  8.6805-      (t
  8.6806-       (parameter-error "Invalid ~S destination ~S" 'slurp-input-stream x)))))
  8.6807-
  8.6808-;;;; Vomiting a stream, typically into the input of another program.
  8.6809-(with-upgradability ()
  8.6810-  (defgeneric vomit-output-stream (processor output-stream &key)
  8.6811-    (:documentation
  8.6812-     "VOMIT-OUTPUT-STREAM is a generic function with two positional arguments
  8.6813-PROCESSOR and OUTPUT-STREAM and additional keyword arguments, that produces (vomits)
  8.6814-some content onto the OUTPUT-STREAM, according to a method specified by PROCESSOR.
  8.6815-
  8.6816-Built-in methods include the following:
  8.6817-* if PROCESSOR is a function, it is called with the OUTPUT-STREAM as its argument
  8.6818-* if PROCESSOR is a list, its first element should be a function.
  8.6819-  It will be applied to a cons of the OUTPUT-STREAM and the rest of the list.
  8.6820-  That is (x . y) will be treated as \(APPLY x <stream> y\)
  8.6821-* if PROCESSOR is an input-stream, its contents will be copied the OUTPUT-STREAM,
  8.6822-  per copy-stream-to-stream, with appropriate keyword arguments.
  8.6823-* if PROCESSOR is a string, its contents will be printed to the OUTPUT-STREAM.
  8.6824-* if PROCESSOR is T, it is treated the same as *standard-input*. If it is NIL, nothing is done.
  8.6825-
  8.6826-Programmers are encouraged to define their own methods for this generic function."))
  8.6827-
  8.6828-  #-genera
  8.6829-  (defmethod vomit-output-stream ((function function) output-stream &key)
  8.6830-    (funcall function output-stream))
  8.6831-
  8.6832-  (defmethod vomit-output-stream ((list cons) output-stream &key)
  8.6833-    (apply (first list) output-stream (rest list)))
  8.6834-
  8.6835-  #-genera
  8.6836-  (defmethod vomit-output-stream ((input-stream stream) output-stream
  8.6837-                                 &key linewise prefix (element-type 'character) buffer-size)
  8.6838-    (copy-stream-to-stream
  8.6839-     input-stream output-stream
  8.6840-     :linewise linewise :prefix prefix :element-type element-type :buffer-size buffer-size))
  8.6841-
  8.6842-  (defmethod vomit-output-stream ((x string) stream &key fresh-line terpri)
  8.6843-    (princ x stream)
  8.6844-    (when fresh-line (fresh-line stream))
  8.6845-    (when terpri (terpri stream))
  8.6846-    (values))
  8.6847-
  8.6848-  (defmethod vomit-output-stream ((x (eql t)) stream &rest keys &key &allow-other-keys)
  8.6849-    (apply 'vomit-output-stream *standard-input* stream keys))
  8.6850-
  8.6851-  (defmethod vomit-output-stream ((x null) (stream t) &key)
  8.6852-    (values))
  8.6853-
  8.6854-  (defmethod vomit-output-stream ((pathname pathname) input
  8.6855-                                 &key
  8.6856-                                   (element-type *default-stream-element-type*)
  8.6857-                                   (external-format *utf-8-external-format*)
  8.6858-                                   (if-exists :rename-and-delete)
  8.6859-                                   (if-does-not-exist :create)
  8.6860-                                   buffer-size
  8.6861-                                   linewise)
  8.6862-    (with-output-file (output pathname
  8.6863-                              :element-type element-type
  8.6864-                              :external-format external-format
  8.6865-                              :if-exists if-exists
  8.6866-                              :if-does-not-exist if-does-not-exist)
  8.6867-      (copy-stream-to-stream
  8.6868-       input output
  8.6869-       :element-type element-type :buffer-size buffer-size :linewise linewise)))
  8.6870-
  8.6871-  (defmethod vomit-output-stream (x stream
  8.6872-                                 &key linewise prefix (element-type 'character) buffer-size)
  8.6873-    (declare (ignorable stream linewise prefix element-type buffer-size))
  8.6874-    (cond
  8.6875-      #+genera
  8.6876-      ((functionp x) (funcall x stream))
  8.6877-      #+genera
  8.6878-      ((input-stream-p x)
  8.6879-       (copy-stream-to-stream
  8.6880-        x stream
  8.6881-        :linewise linewise :prefix prefix :element-type element-type :buffer-size buffer-size))
  8.6882-      (t
  8.6883-       (parameter-error "Invalid ~S source ~S" 'vomit-output-stream x)))))
  8.6884-
  8.6885-
  8.6886-;;;; Run-program: synchronously run a program in a subprocess, handling input, output and error-output.
  8.6887-(with-upgradability ()
  8.6888-  (define-condition subprocess-error (error)
  8.6889-    ((code :initform nil :initarg :code :reader subprocess-error-code)
  8.6890-     (command :initform nil :initarg :command :reader subprocess-error-command)
  8.6891-     (process :initform nil :initarg :process :reader subprocess-error-process))
  8.6892-    (:report (lambda (condition stream)
  8.6893-               (format stream "Subprocess ~@[~S~% ~]~@[with command ~S~% ~]exited with error~@[ code ~D~]"
  8.6894-                       (subprocess-error-process condition)
  8.6895-                       (subprocess-error-command condition)
  8.6896-                       (subprocess-error-code condition)))))
  8.6897-
  8.6898-  (defun %check-result (exit-code &key command process ignore-error-status)
  8.6899-    (unless ignore-error-status
  8.6900-      (unless (eql exit-code 0)
  8.6901-        (cerror "IGNORE-ERROR-STATUS"
  8.6902-                'subprocess-error :command command :code exit-code :process process)))
  8.6903-    exit-code)
  8.6904-
  8.6905-  (defun %active-io-specifier-p (specifier)
  8.6906-    "Determines whether a run-program I/O specifier requires Lisp-side processing
  8.6907-via SLURP-INPUT-STREAM or VOMIT-OUTPUT-STREAM (return T),
  8.6908-or whether it's already taken care of by the implementation's underlying run-program."
  8.6909-    (not (typep specifier '(or null string pathname (member :interactive :output)
  8.6910-                            #+(or cmucl (and sbcl os-unix) scl) (or stream (eql t))
  8.6911-                            #+lispworks file-stream))))
  8.6912-
  8.6913-  (defun %run-program (command &rest keys &key &allow-other-keys)
  8.6914-    "DEPRECATED. Use LAUNCH-PROGRAM instead."
  8.6915-    (apply 'launch-program command keys))
  8.6916-
  8.6917-  (defun %call-with-program-io (gf tval stream-easy-p fun direction spec activep returner
  8.6918-                                &key
  8.6919-                                  (element-type #-clozure *default-stream-element-type* #+clozure 'character)
  8.6920-                                  (external-format *utf-8-external-format*) &allow-other-keys)
  8.6921-    ;; handle redirection for run-program and system
  8.6922-    ;; SPEC is the specification for the subprocess's input or output or error-output
  8.6923-    ;; TVAL is the value used if the spec is T
  8.6924-    ;; GF is the generic function to call to handle arbitrary values of SPEC
  8.6925-    ;; STREAM-EASY-P is T if we're going to use a RUN-PROGRAM that copies streams in the background
  8.6926-    ;; (it's only meaningful on CMUCL, SBCL, SCL that actually do it)
  8.6927-    ;; DIRECTION is :INPUT, :OUTPUT or :ERROR-OUTPUT for the direction of this io argument
  8.6928-    ;; FUN is a function of the new reduced spec and an activity function to call with a stream
  8.6929-    ;; when the subprocess is active and communicating through that stream.
  8.6930-    ;; ACTIVEP is a boolean true if we will get to run code while the process is running
  8.6931-    ;; ELEMENT-TYPE and EXTERNAL-FORMAT control what kind of temporary file we may open.
  8.6932-    ;; RETURNER is a function called with the value of the activity.
  8.6933-    ;; --- TODO (fare@tunes.org): handle if-output-exists and such when doing it the hard way.
  8.6934-    (declare (ignorable stream-easy-p))
  8.6935-    (let* ((actual-spec (if (eq spec t) tval spec))
  8.6936-           (activity-spec (if (eq actual-spec :output)
  8.6937-                              (ecase direction
  8.6938-                                ((:input :output)
  8.6939-                                 (parameter-error "~S does not allow ~S as a ~S spec"
  8.6940-                                                  'run-program :output direction))
  8.6941-                                ((:error-output)
  8.6942-                                 nil))
  8.6943-                              actual-spec)))
  8.6944-      (labels ((activity (stream)
  8.6945-                 (call-function returner (call-stream-processor gf activity-spec stream)))
  8.6946-               (easy-case ()
  8.6947-                 (funcall fun actual-spec nil))
  8.6948-               (hard-case ()
  8.6949-                 (if activep
  8.6950-                     (funcall fun :stream #'activity)
  8.6951-                     (with-temporary-file (:pathname tmp)
  8.6952-                       (ecase direction
  8.6953-                         (:input
  8.6954-                          (with-output-file (s tmp :if-exists :overwrite
  8.6955-                                               :external-format external-format
  8.6956-                                               :element-type element-type)
  8.6957-                            (activity s))
  8.6958-                          (funcall fun tmp nil))
  8.6959-                         ((:output :error-output)
  8.6960-                          (multiple-value-prog1 (funcall fun tmp nil)
  8.6961-                            (with-input-file (s tmp
  8.6962-                                               :external-format external-format
  8.6963-                                               :element-type element-type)
  8.6964-                              (activity s)))))))))
  8.6965-        (typecase activity-spec
  8.6966-          ((or null string pathname (eql :interactive))
  8.6967-           (easy-case))
  8.6968-          #+(or cmucl (and sbcl os-unix) scl) ;; streams are only easy on implementations that try very hard
  8.6969-          (stream
  8.6970-           (if stream-easy-p (easy-case) (hard-case)))
  8.6971-          (t
  8.6972-           (hard-case))))))
  8.6973-
  8.6974-  (defmacro place-setter (place)
  8.6975-    (when place
  8.6976-      (let ((value (gensym)))
  8.6977-        `#'(lambda (,value) (setf ,place ,value)))))
  8.6978-
  8.6979-  (defmacro with-program-input (((reduced-input-var
  8.6980-                                  &optional (input-activity-var (gensym) iavp))
  8.6981-                                 input-form &key setf stream-easy-p active keys) &body body)
  8.6982-    `(apply '%call-with-program-io 'vomit-output-stream *standard-input* ,stream-easy-p
  8.6983-            #'(lambda (,reduced-input-var ,input-activity-var)
  8.6984-                ,@(unless iavp `((declare (ignore ,input-activity-var))))
  8.6985-                ,@body)
  8.6986-            :input ,input-form ,active (place-setter ,setf) ,keys))
  8.6987-
  8.6988-  (defmacro with-program-output (((reduced-output-var
  8.6989-                                  &optional (output-activity-var (gensym) oavp))
  8.6990-                                  output-form &key setf stream-easy-p active keys) &body body)
  8.6991-    `(apply '%call-with-program-io 'slurp-input-stream *standard-output* ,stream-easy-p
  8.6992-            #'(lambda (,reduced-output-var ,output-activity-var)
  8.6993-                ,@(unless oavp `((declare (ignore ,output-activity-var))))
  8.6994-                ,@body)
  8.6995-            :output ,output-form ,active (place-setter ,setf) ,keys))
  8.6996-
  8.6997-  (defmacro with-program-error-output (((reduced-error-output-var
  8.6998-                                         &optional (error-output-activity-var (gensym) eoavp))
  8.6999-                                        error-output-form &key setf stream-easy-p active keys)
  8.7000-                                       &body body)
  8.7001-    `(apply '%call-with-program-io 'slurp-input-stream *error-output* ,stream-easy-p
  8.7002-            #'(lambda (,reduced-error-output-var ,error-output-activity-var)
  8.7003-                ,@(unless eoavp `((declare (ignore ,error-output-activity-var))))
  8.7004-                ,@body)
  8.7005-            :error-output ,error-output-form ,active (place-setter ,setf) ,keys))
  8.7006-
  8.7007-  (defun %use-launch-program (command &rest keys
  8.7008-                           &key input output error-output ignore-error-status &allow-other-keys)
  8.7009-    ;; helper for RUN-PROGRAM when using LAUNCH-PROGRAM
  8.7010-    #+(or cormanlisp gcl (and lispworks os-windows) mcl xcl)
  8.7011-    (progn
  8.7012-      command keys input output error-output ignore-error-status ;; ignore
  8.7013-      (not-implemented-error '%use-launch-program))
  8.7014-    (when (member :stream (list input output error-output))
  8.7015-      (parameter-error "~S: ~S is not allowed as synchronous I/O redirection argument"
  8.7016-                       'run-program :stream))
  8.7017-    (let* ((active-input-p (%active-io-specifier-p input))
  8.7018-           (active-output-p (%active-io-specifier-p output))
  8.7019-           (active-error-output-p (%active-io-specifier-p error-output))
  8.7020-           (activity
  8.7021-             (cond
  8.7022-               (active-output-p :output)
  8.7023-               (active-input-p :input)
  8.7024-               (active-error-output-p :error-output)
  8.7025-               (t nil)))
  8.7026-           output-result error-output-result exit-code process-info)
  8.7027-      (with-program-output ((reduced-output output-activity)
  8.7028-                            output :keys keys :setf output-result
  8.7029-                            :stream-easy-p t :active (eq activity :output))
  8.7030-        (with-program-error-output ((reduced-error-output error-output-activity)
  8.7031-                                    error-output :keys keys :setf error-output-result
  8.7032-                                    :stream-easy-p t :active (eq activity :error-output))
  8.7033-          (with-program-input ((reduced-input input-activity)
  8.7034-                               input :keys keys
  8.7035-                               :stream-easy-p t :active (eq activity :input))
  8.7036-            (setf process-info
  8.7037-                  (apply 'launch-program command
  8.7038-                         :input reduced-input :output reduced-output
  8.7039-                         :error-output (if (eq error-output :output) :output reduced-error-output)
  8.7040-                         keys))
  8.7041-            (labels ((get-stream (stream-name &optional fallbackp)
  8.7042-                       (or (slot-value process-info stream-name)
  8.7043-                           (when fallbackp
  8.7044-                             (slot-value process-info 'bidir-stream))))
  8.7045-                     (run-activity (activity stream-name &optional fallbackp)
  8.7046-                       (if-let (stream (get-stream stream-name fallbackp))
  8.7047-                         (funcall activity stream)
  8.7048-                         (error 'subprocess-error
  8.7049-                                :code `(:missing ,stream-name)
  8.7050-                                :command command :process process-info))))
  8.7051-              (unwind-protect
  8.7052-                   (ecase activity
  8.7053-                     ((nil))
  8.7054-                     (:input (run-activity input-activity 'input-stream t))
  8.7055-                     (:output (run-activity output-activity 'output-stream t))
  8.7056-                     (:error-output (run-activity error-output-activity 'error-output-stream)))
  8.7057-                (close-streams process-info)
  8.7058-                (setf exit-code (wait-process process-info)))))))
  8.7059-      (%check-result exit-code
  8.7060-                     :command command :process process-info
  8.7061-                     :ignore-error-status ignore-error-status)
  8.7062-      (values output-result error-output-result exit-code)))
  8.7063-
  8.7064-  (defun %normalize-system-command (command) ;; helper for %USE-SYSTEM
  8.7065-    (etypecase command
  8.7066-      (string command)
  8.7067-      (list (escape-shell-command
  8.7068-             (os-cond
  8.7069-              ((os-unix-p) (cons "exec" command))
  8.7070-              (t command))))))
  8.7071-
  8.7072-  (defun %redirected-system-command (command in out err directory) ;; helper for %USE-SYSTEM
  8.7073-    (flet ((redirect (spec operator)
  8.7074-             (let ((pathname
  8.7075-                     (typecase spec
  8.7076-                       (null (null-device-pathname))
  8.7077-                       (string (parse-native-namestring spec))
  8.7078-                       (pathname spec)
  8.7079-                       ((eql :output)
  8.7080-                        (unless (equal operator " 2>>")
  8.7081-                          (parameter-error "~S: only the ~S argument can be ~S"
  8.7082-                                           'run-program :error-output :output))
  8.7083-                        (return-from redirect '(" 2>&1"))))))
  8.7084-               (when pathname
  8.7085-                 (list operator " "
  8.7086-                       (escape-shell-token (native-namestring pathname)))))))
  8.7087-      (let* ((redirections (append (redirect in " <") (redirect out " >>") (redirect err " 2>>")))
  8.7088-             (normalized (%normalize-system-command command))
  8.7089-             (directory (or directory #+(or abcl xcl) (getcwd)))
  8.7090-             (chdir (when directory
  8.7091-                      (let ((dir-arg (escape-shell-token (native-namestring directory))))
  8.7092-                        (os-cond
  8.7093-                         ((os-unix-p) `("cd " ,dir-arg " ; "))
  8.7094-                         ((os-windows-p) `("cd /d " ,dir-arg " & ")))))))
  8.7095-        (reduce/strcat
  8.7096-         (os-cond
  8.7097-          ((os-unix-p) `(,@(when redirections `("exec " ,@redirections " ; ")) ,@chdir ,normalized))
  8.7098-          ((os-windows-p) `(,@redirections " (" ,@chdir ,normalized ")")))))))
  8.7099-
  8.7100-  (defun %system (command &rest keys &key directory
  8.7101-                                       input (if-input-does-not-exist :error)
  8.7102-                                       output (if-output-exists :supersede)
  8.7103-                                       error-output (if-error-output-exists :supersede)
  8.7104-                                       &allow-other-keys)
  8.7105-    "A portable abstraction of a low-level call to libc's system()."
  8.7106-    (declare (ignorable keys directory input if-input-does-not-exist output
  8.7107-                        if-output-exists error-output if-error-output-exists))
  8.7108-    (when (member :stream (list input output error-output))
  8.7109-      (parameter-error "~S: ~S is not allowed as synchronous I/O redirection argument"
  8.7110-                       'run-program :stream))
  8.7111-    #+(or abcl allegro clozure cmucl ecl (and lispworks os-unix) mkcl sbcl scl)
  8.7112-    (let (#+(or abcl ecl mkcl)
  8.7113-            (version (parse-version
  8.7114-                      #-abcl
  8.7115-                      (lisp-implementation-version)
  8.7116-                      #+abcl
  8.7117-                      (second (split-string (implementation-identifier) :separator '(#\-))))))
  8.7118-      (nest
  8.7119-       #+abcl (unless (lexicographic< '< version '(1 4 0)))
  8.7120-       #+ecl (unless (lexicographic<= '< version '(16 0 0)))
  8.7121-       #+mkcl (unless (lexicographic<= '< version '(1 1 9)))
  8.7122-       (return-from %system
  8.7123-         (wait-process
  8.7124-          (apply 'launch-program (%normalize-system-command command) keys)))))
  8.7125-    #+(or abcl clasp clisp cormanlisp ecl gcl genera (and lispworks os-windows) mkcl xcl)
  8.7126-    (let ((%command (%redirected-system-command command input output error-output directory)))
  8.7127-      ;; see comments for these functions
  8.7128-      (%handle-if-does-not-exist input if-input-does-not-exist)
  8.7129-      (%handle-if-exists output if-output-exists)
  8.7130-      (%handle-if-exists error-output if-error-output-exists)
  8.7131-      #+abcl (ext:run-shell-command %command)
  8.7132-      #+(or clasp ecl) (let ((*standard-input* *stdin*)
  8.7133-                             (*standard-output* *stdout*)
  8.7134-                             (*error-output* *stderr*))
  8.7135-                         (ext:system %command))
  8.7136-      #+clisp
  8.7137-      (let ((raw-exit-code
  8.7138-             (or
  8.7139-              #.`(#+os-windows ,@'(ext:run-shell-command %command)
  8.7140-                  #+os-unix ,@'(ext:run-program "/bin/sh" :arguments `("-c" ,%command))
  8.7141-                  :wait t :input :terminal :output :terminal)
  8.7142-              0)))
  8.7143-        (if (minusp raw-exit-code)
  8.7144-            (- 128 raw-exit-code)
  8.7145-            raw-exit-code))
  8.7146-      #+cormanlisp (win32:system %command)
  8.7147-      #+gcl (system:system %command)
  8.7148-      #+genera (not-implemented-error '%system)
  8.7149-      #+(and lispworks os-windows)
  8.7150-      (system:call-system %command :current-directory directory :wait t)
  8.7151-      #+mcl (ccl::with-cstrs ((%%command %command)) (_system %%command))
  8.7152-      #+mkcl (mkcl:system %command)
  8.7153-      #+xcl (system:%run-shell-command %command)))
  8.7154-
  8.7155-  (defun %use-system (command &rest keys
  8.7156-                      &key input output error-output ignore-error-status &allow-other-keys)
  8.7157-    ;; helper for RUN-PROGRAM when using %system
  8.7158-    (let (output-result error-output-result exit-code)
  8.7159-      (with-program-output ((reduced-output)
  8.7160-                            output :keys keys :setf output-result)
  8.7161-        (with-program-error-output ((reduced-error-output)
  8.7162-                                    error-output :keys keys :setf error-output-result)
  8.7163-          (with-program-input ((reduced-input) input :keys keys)
  8.7164-            (setf exit-code (apply '%system command
  8.7165-                                   :input reduced-input :output reduced-output
  8.7166-                                   :error-output reduced-error-output keys)))))
  8.7167-      (%check-result exit-code
  8.7168-                     :command command
  8.7169-                     :ignore-error-status ignore-error-status)
  8.7170-      (values output-result error-output-result exit-code)))
  8.7171-
  8.7172-  (defun run-program (command &rest keys
  8.7173-                       &key ignore-error-status (force-shell nil force-shell-suppliedp)
  8.7174-                         input (if-input-does-not-exist :error)
  8.7175-                         output (if-output-exists :supersede)
  8.7176-                         error-output (if-error-output-exists :supersede)
  8.7177-                         (element-type #-clozure *default-stream-element-type* #+clozure 'character)
  8.7178-                         (external-format *utf-8-external-format*)
  8.7179-                       &allow-other-keys)
  8.7180-    "Run program specified by COMMAND,
  8.7181-either a list of strings specifying a program and list of arguments,
  8.7182-or a string specifying a shell command (/bin/sh on Unix, CMD.EXE on Windows);
  8.7183-_synchronously_ process its output as specified and return the processing results
  8.7184-when the program and its output processing are complete.
  8.7185-
  8.7186-Always call a shell (rather than directly execute the command when possible)
  8.7187-if FORCE-SHELL is specified.  Similarly, never call a shell if FORCE-SHELL is
  8.7188-specified to be NIL.
  8.7189-
  8.7190-Signal a continuable SUBPROCESS-ERROR if the process wasn't successful (exit-code 0),
  8.7191-unless IGNORE-ERROR-STATUS is specified.
  8.7192-
  8.7193-If OUTPUT is a pathname, a string designating a pathname, or NIL (the default)
  8.7194-designating the null device, the file at that path is used as output.
  8.7195-If it's :INTERACTIVE, output is inherited from the current process;
  8.7196-beware that this may be different from your *STANDARD-OUTPUT*,
  8.7197-and under SLIME will be on your *inferior-lisp* buffer.
  8.7198-If it's T, output goes to your current *STANDARD-OUTPUT* stream.
  8.7199-Otherwise, OUTPUT should be a value that is a suitable first argument to
  8.7200-SLURP-INPUT-STREAM (qv.), or a list of such a value and keyword arguments.
  8.7201-In this case, RUN-PROGRAM will create a temporary stream for the program output;
  8.7202-the program output, in that stream, will be processed by a call to SLURP-INPUT-STREAM,
  8.7203-using OUTPUT as the first argument (or the first element of OUTPUT, and the rest as keywords).
  8.7204-The primary value resulting from that call (or NIL if no call was needed)
  8.7205-will be the first value returned by RUN-PROGRAM.
  8.7206-E.g., using :OUTPUT :STRING will have it return the entire output stream as a string.
  8.7207-And using :OUTPUT '(:STRING :STRIPPED T) will have it return the same string
  8.7208-stripped of any ending newline.
  8.7209-
  8.7210-IF-OUTPUT-EXISTS, which is only meaningful if OUTPUT is a string or a
  8.7211-pathname, can take the values :ERROR, :APPEND, and :SUPERSEDE (the
  8.7212-default). The meaning of these values and their effect on the case
  8.7213-where OUTPUT does not exist, is analogous to the IF-EXISTS parameter
  8.7214-to OPEN with :DIRECTION :OUTPUT.
  8.7215-
  8.7216-ERROR-OUTPUT is similar to OUTPUT, except that the resulting value is returned
  8.7217-as the second value of RUN-PROGRAM. T designates the *ERROR-OUTPUT*.
  8.7218-Also :OUTPUT means redirecting the error output to the output stream,
  8.7219-in which case NIL is returned.
  8.7220-
  8.7221-IF-ERROR-OUTPUT-EXISTS is similar to IF-OUTPUT-EXIST, except that it
  8.7222-affects ERROR-OUTPUT rather than OUTPUT.
  8.7223-
  8.7224-INPUT is similar to OUTPUT, except that VOMIT-OUTPUT-STREAM is used,
  8.7225-no value is returned, and T designates the *STANDARD-INPUT*.
  8.7226-
  8.7227-IF-INPUT-DOES-NOT-EXIST, which is only meaningful if INPUT is a string
  8.7228-or a pathname, can take the values :CREATE and :ERROR (the
  8.7229-default). The meaning of these values is analogous to the
  8.7230-IF-DOES-NOT-EXIST parameter to OPEN with :DIRECTION :INPUT.
  8.7231-
  8.7232-ELEMENT-TYPE and EXTERNAL-FORMAT are passed on
  8.7233-to your Lisp implementation, when applicable, for creation of the output stream.
  8.7234-
  8.7235-One and only one of the stream slurping or vomiting may or may not happen
  8.7236-in parallel in parallel with the subprocess,
  8.7237-depending on options and implementation,
  8.7238-and with priority being given to output processing.
  8.7239-Other streams are completely produced or consumed
  8.7240-before or after the subprocess is spawned, using temporary files.
  8.7241-
  8.7242-RUN-PROGRAM returns 3 values:
  8.7243-0- the result of the OUTPUT slurping if any, or NIL
  8.7244-1- the result of the ERROR-OUTPUT slurping if any, or NIL
  8.7245-2- either 0 if the subprocess exited with success status,
  8.7246-or an indication of failure via the EXIT-CODE of the process"
  8.7247-    (declare (ignorable input output error-output if-input-does-not-exist if-output-exists
  8.7248-                        if-error-output-exists element-type external-format ignore-error-status))
  8.7249-    #-(or abcl allegro clasp clisp clozure cmucl cormanlisp ecl gcl lispworks mcl mkcl sbcl scl xcl)
  8.7250-    (not-implemented-error 'run-program)
  8.7251-    (apply (if (or force-shell
  8.7252-                   ;; Per doc string, set FORCE-SHELL to T if we get command as a string.
  8.7253-                   ;; But don't override user's specified preference. [2015/06/29:rpg]
  8.7254-                   (and (stringp command)
  8.7255-                        (or (not force-shell-suppliedp)
  8.7256-                            #-(or allegro clisp clozure sbcl) (os-cond ((os-windows-p) t))))
  8.7257-                   #+(or clasp clisp cormanlisp gcl (and lispworks os-windows) mcl xcl) t
  8.7258-                   ;; A race condition in ECL <= 16.0.0 prevents using ext:run-program
  8.7259-                   #+ecl #.(if-let (ver (parse-version (lisp-implementation-version)))
  8.7260-                                   (lexicographic<= '< ver '(16 0 0)))
  8.7261-                   #+(and lispworks os-unix) (%interactivep input output error-output))
  8.7262-               '%use-system '%use-launch-program)
  8.7263-           command keys)))
  8.7264-
  8.7265-;;;; ---------------------------------------------------------------------------
  8.7266-;;;; Generic support for configuration files
  8.7267-
  8.7268-(uiop/package:define-package :uiop/configuration
  8.7269-  (:recycle :uiop/configuration :asdf/configuration) ;; necessary to upgrade from 2.27.
  8.7270-  (:use :uiop/package :uiop/common-lisp :uiop/utility
  8.7271-   :uiop/os :uiop/pathname :uiop/filesystem :uiop/stream :uiop/image :uiop/lisp-build)
  8.7272-  (:export
  8.7273-   #:user-configuration-directories #:system-configuration-directories ;; implemented in backward-driver
  8.7274-   #:in-first-directory #:in-user-configuration-directory #:in-system-configuration-directory ;; idem
  8.7275-   #:get-folder-path
  8.7276-   #:xdg-data-home #:xdg-config-home #:xdg-data-dirs #:xdg-config-dirs
  8.7277-   #:xdg-cache-home #:xdg-runtime-dir #:system-config-pathnames
  8.7278-   #:filter-pathname-set #:xdg-data-pathnames #:xdg-config-pathnames
  8.7279-   #:find-preferred-file #:xdg-data-pathname #:xdg-config-pathname
  8.7280-   #:validate-configuration-form #:validate-configuration-file #:validate-configuration-directory
  8.7281-   #:configuration-inheritance-directive-p
  8.7282-   #:report-invalid-form #:invalid-configuration #:*ignored-configuration-form* #:*user-cache*
  8.7283-   #:*clear-configuration-hook* #:clear-configuration #:register-clear-configuration-hook
  8.7284-   #:resolve-location #:location-designator-p #:location-function-p #:*here-directory*
  8.7285-   #:resolve-relative-location #:resolve-absolute-location #:upgrade-configuration
  8.7286-   #:uiop-directory))
  8.7287-(in-package :uiop/configuration)
  8.7288-
  8.7289-(with-upgradability ()
  8.7290-  (define-condition invalid-configuration ()
  8.7291-    ((form :reader condition-form :initarg :form)
  8.7292-     (location :reader condition-location :initarg :location)
  8.7293-     (format :reader condition-format :initarg :format)
  8.7294-     (arguments :reader condition-arguments :initarg :arguments :initform nil))
  8.7295-    (:report (lambda (c s)
  8.7296-               (format s (compatfmt "~@<~? (will be skipped)~@:>")
  8.7297-                       (condition-format c)
  8.7298-                       (list* (condition-form c) (condition-location c)
  8.7299-                              (condition-arguments c))))))
  8.7300-
  8.7301-  (defun configuration-inheritance-directive-p (x)
  8.7302-    "Is X a configuration inheritance directive?"
  8.7303-    (let ((kw '(:inherit-configuration :ignore-inherited-configuration)))
  8.7304-      (or (member x kw)
  8.7305-          (and (length=n-p x 1) (member (car x) kw)))))
  8.7306-
  8.7307-  (defun report-invalid-form (reporter &rest args)
  8.7308-    "Report an invalid form according to REPORTER and various ARGS"
  8.7309-    (etypecase reporter
  8.7310-      (null
  8.7311-       (apply 'error 'invalid-configuration args))
  8.7312-      (function
  8.7313-       (apply reporter args))
  8.7314-      ((or symbol string)
  8.7315-       (apply 'error reporter args))
  8.7316-      (cons
  8.7317-       (apply 'apply (append reporter args)))))
  8.7318-
  8.7319-  (defvar *ignored-configuration-form* nil
  8.7320-    "Have configuration forms been ignored while parsing the configuration?")
  8.7321-
  8.7322-  (defun validate-configuration-form (form tag directive-validator
  8.7323-                                            &key location invalid-form-reporter)
  8.7324-    "Validate a configuration FORM. By default it will raise an error if the
  8.7325-FORM is not valid.  Otherwise it will return the validated form.
  8.7326-     Arguments control the behavior:
  8.7327-     The configuration FORM should be of the form (TAG . <rest>)
  8.7328-     Each element of <rest> will be checked by first seeing if it's a configuration inheritance
  8.7329-directive (see CONFIGURATION-INHERITANCE-DIRECTIVE-P) then invoking DIRECTIVE-VALIDATOR
  8.7330-on it.
  8.7331-     In the event of an invalid form, INVALID-FORM-REPORTER will be used to control
  8.7332-reporting (see REPORT-INVALID-FORM) with LOCATION providing information about where
  8.7333-the configuration form appeared."
  8.7334-    (unless (and (consp form) (eq (car form) tag))
  8.7335-      (setf *ignored-configuration-form* t)
  8.7336-      (report-invalid-form invalid-form-reporter :form form :location location)
  8.7337-      (return-from validate-configuration-form nil))
  8.7338-    (loop :with inherit = 0 :with ignore-invalid-p = nil :with x = (list tag)
  8.7339-          :for directive :in (cdr form)
  8.7340-          :when (cond
  8.7341-                  ((configuration-inheritance-directive-p directive)
  8.7342-                   (incf inherit) t)
  8.7343-                  ((eq directive :ignore-invalid-entries)
  8.7344-                   (setf ignore-invalid-p t) t)
  8.7345-                  ((funcall directive-validator directive)
  8.7346-                   t)
  8.7347-                  (ignore-invalid-p
  8.7348-                   nil)
  8.7349-                  (t
  8.7350-                   (setf *ignored-configuration-form* t)
  8.7351-                   (report-invalid-form invalid-form-reporter :form directive :location location)
  8.7352-                   nil))
  8.7353-            :do (push directive x)
  8.7354-          :finally
  8.7355-             (unless (= inherit 1)
  8.7356-               (report-invalid-form invalid-form-reporter
  8.7357-                                    :form form :location location
  8.7358-                                    ;; we throw away the form and location arguments, hence the ~2*
  8.7359-                                    ;; this is necessary because of the report in INVALID-CONFIGURATION
  8.7360-                                    :format (compatfmt "~@<Invalid source registry ~S~@[ in ~S~]. ~
  8.7361-                                                        One and only one of ~S or ~S is required.~@:>")
  8.7362-                                    :arguments '(:inherit-configuration :ignore-inherited-configuration)))
  8.7363-             (return (nreverse x))))
  8.7364-
  8.7365-  (defun validate-configuration-file (file validator &key description)
  8.7366-    "Validate a configuration FILE.  The configuration file should have only one s-expression
  8.7367-in it, which will be checked with the VALIDATOR FORM.  DESCRIPTION argument used for error
  8.7368-reporting."
  8.7369-    (let ((forms (read-file-forms file)))
  8.7370-      (unless (length=n-p forms 1)
  8.7371-        (error (compatfmt "~@<One and only one form allowed for ~A. Got: ~3i~_~S~@:>~%")
  8.7372-               description forms))
  8.7373-      (funcall validator (car forms) :location file)))
  8.7374-
  8.7375-  (defun validate-configuration-directory (directory tag validator &key invalid-form-reporter)
  8.7376-    "Map the VALIDATOR across the .conf files in DIRECTORY, the TAG will
  8.7377-be applied to the results to yield a configuration form.  Current
  8.7378-values of TAG include :source-registry and :output-translations."
  8.7379-    (let ((files (sort (ignore-errors ;; SORT w/o COPY-LIST is OK: DIRECTORY returns a fresh list
  8.7380-                        (remove-if
  8.7381-                         'hidden-pathname-p
  8.7382-                         (directory* (make-pathname :name *wild* :type "conf" :defaults directory))))
  8.7383-                       #'string< :key #'namestring)))
  8.7384-      `(,tag
  8.7385-        ,@(loop :for file :in files :append
  8.7386-                                    (loop :with ignore-invalid-p = nil
  8.7387-                                          :for form :in (read-file-forms file)
  8.7388-                                          :when (eq form :ignore-invalid-entries)
  8.7389-                                            :do (setf ignore-invalid-p t)
  8.7390-                                          :else
  8.7391-                                            :when (funcall validator form)
  8.7392-                                              :collect form
  8.7393-                                          :else
  8.7394-                                            :when ignore-invalid-p
  8.7395-                                              :do (setf *ignored-configuration-form* t)
  8.7396-                                          :else
  8.7397-                                            :do (report-invalid-form invalid-form-reporter :form form :location file)))
  8.7398-        :inherit-configuration)))
  8.7399-
  8.7400-  (defun resolve-relative-location (x &key ensure-directory wilden)
  8.7401-    "Given a designator X for an relative location, resolve it to a pathname."
  8.7402-    (ensure-pathname
  8.7403-     (etypecase x
  8.7404-       (null nil)
  8.7405-       (pathname x)
  8.7406-       (string (parse-unix-namestring
  8.7407-                x :ensure-directory ensure-directory))
  8.7408-       (cons
  8.7409-        (if (null (cdr x))
  8.7410-            (resolve-relative-location
  8.7411-             (car x) :ensure-directory ensure-directory :wilden wilden)
  8.7412-            (let* ((car (resolve-relative-location
  8.7413-                         (car x) :ensure-directory t :wilden nil)))
  8.7414-              (merge-pathnames*
  8.7415-               (resolve-relative-location
  8.7416-                (cdr x) :ensure-directory ensure-directory :wilden wilden)
  8.7417-               car))))
  8.7418-       ((eql :*/) *wild-directory*)
  8.7419-       ((eql :**/) *wild-inferiors*)
  8.7420-       ((eql :*.*.*) *wild-file*)
  8.7421-       ((eql :implementation)
  8.7422-        (parse-unix-namestring
  8.7423-         (implementation-identifier) :ensure-directory t))
  8.7424-       ((eql :implementation-type)
  8.7425-        (parse-unix-namestring
  8.7426-         (string-downcase (implementation-type)) :ensure-directory t))
  8.7427-       ((eql :hostname)
  8.7428-        (parse-unix-namestring (hostname) :ensure-directory t)))
  8.7429-     :wilden (and wilden (not (pathnamep x)) (not (member x '(:*/ :**/ :*.*.*))))
  8.7430-     :want-relative t))
  8.7431-
  8.7432-  (defvar *here-directory* nil
  8.7433-    "This special variable is bound to the currect directory during calls to
  8.7434-PROCESS-SOURCE-REGISTRY in order that we be able to interpret the :here
  8.7435-directive.")
  8.7436-
  8.7437-  (defvar *user-cache* nil
  8.7438-    "A specification as per RESOLVE-LOCATION of where the user keeps his FASL cache")
  8.7439-
  8.7440-  (defun resolve-absolute-location (x &key ensure-directory wilden)
  8.7441-    "Given a designator X for an absolute location, resolve it to a pathname"
  8.7442-    (ensure-pathname
  8.7443-     (etypecase x
  8.7444-       (null nil)
  8.7445-       (pathname x)
  8.7446-       (string
  8.7447-        (let ((p #-mcl (parse-namestring x)
  8.7448-                 #+mcl (probe-posix x)))
  8.7449-          #+mcl (unless p (error "POSIX pathname ~S does not exist" x))
  8.7450-          (if ensure-directory (ensure-directory-pathname p) p)))
  8.7451-       (cons
  8.7452-        (return-from resolve-absolute-location
  8.7453-          (if (null (cdr x))
  8.7454-              (resolve-absolute-location
  8.7455-               (car x) :ensure-directory ensure-directory :wilden wilden)
  8.7456-              (merge-pathnames*
  8.7457-               (resolve-relative-location
  8.7458-                (cdr x) :ensure-directory ensure-directory :wilden wilden)
  8.7459-               (resolve-absolute-location
  8.7460-                (car x) :ensure-directory t :wilden nil)))))
  8.7461-       ((eql :root)
  8.7462-        ;; special magic! we return a relative pathname,
  8.7463-        ;; but what it means to the output-translations is
  8.7464-        ;; "relative to the root of the source pathname's host and device".
  8.7465-        (return-from resolve-absolute-location
  8.7466-          (let ((p (make-pathname :directory '(:relative))))
  8.7467-            (if wilden (wilden p) p))))
  8.7468-       ((eql :home) (user-homedir-pathname))
  8.7469-       ((eql :here) (resolve-absolute-location
  8.7470-                     (or *here-directory* (pathname-directory-pathname (truename (load-pathname))))
  8.7471-                     :ensure-directory t :wilden nil))
  8.7472-       ((eql :user-cache) (resolve-absolute-location
  8.7473-                           *user-cache* :ensure-directory t :wilden nil)))
  8.7474-     :wilden (and wilden (not (pathnamep x)))
  8.7475-     :resolve-symlinks *resolve-symlinks*
  8.7476-     :want-absolute t))
  8.7477-
  8.7478-  ;; Try to override declaration in previous versions of ASDF.
  8.7479-  (declaim (ftype (function (t &key (:directory boolean) (:wilden boolean)
  8.7480-                               (:ensure-directory boolean)) t) resolve-location))
  8.7481-
  8.7482-  (defun resolve-location (x &key ensure-directory wilden directory)
  8.7483-    "Resolve location designator X into a PATHNAME"
  8.7484-    ;; :directory backward compatibility, until 2014-01-16: accept directory as well as ensure-directory
  8.7485-    (loop :with dirp = (or directory ensure-directory)
  8.7486-          :with (first . rest) = (if (atom x) (list x) x)
  8.7487-          :with path = (or (resolve-absolute-location
  8.7488-                            first :ensure-directory (and (or dirp rest) t)
  8.7489-                            :wilden (and wilden (null rest)))
  8.7490-                           (return nil))
  8.7491-          :for (element . morep) :on rest
  8.7492-          :for dir = (and (or morep dirp) t)
  8.7493-          :for wild = (and wilden (not morep))
  8.7494-          :for sub = (merge-pathnames*
  8.7495-                      (resolve-relative-location
  8.7496-                       element :ensure-directory dir :wilden wild)
  8.7497-                      path)
  8.7498-          :do (setf path (if (absolute-pathname-p sub) (resolve-symlinks* sub) sub))
  8.7499-          :finally (return path)))
  8.7500-
  8.7501-  (defun location-designator-p (x)
  8.7502-    "Is X a designator for a location?"
  8.7503-    ;; NIL means "skip this entry", or as an output translation, same as translation input.
  8.7504-    ;; T means "any input" for a translation, or as output, same as translation input.
  8.7505-    (flet ((absolute-component-p (c)
  8.7506-             (typep c '(or string pathname
  8.7507-                        (member :root :home :here :user-cache))))
  8.7508-           (relative-component-p (c)
  8.7509-             (typep c '(or string pathname
  8.7510-                        (member :*/ :**/ :*.*.* :implementation :implementation-type)))))
  8.7511-      (or (typep x 'boolean)
  8.7512-          (absolute-component-p x)
  8.7513-          (and (consp x) (absolute-component-p (first x)) (every #'relative-component-p (rest x))))))
  8.7514-
  8.7515-  (defun location-function-p (x)
  8.7516-    "Is X the specification of a location function?"
  8.7517-    ;; Location functions are allowed in output translations, and notably used by ABCL for JAR file support.
  8.7518-    (and (length=n-p x 2) (eq (car x) :function)))
  8.7519-
  8.7520-  (defvar *clear-configuration-hook* '())
  8.7521-
  8.7522-  (defun register-clear-configuration-hook (hook-function &optional call-now-p)
  8.7523-    "Register a function to be called when clearing configuration"
  8.7524-    (register-hook-function '*clear-configuration-hook* hook-function call-now-p))
  8.7525-
  8.7526-  (defun clear-configuration ()
  8.7527-    "Call the functions in *CLEAR-CONFIGURATION-HOOK*"
  8.7528-    (call-functions *clear-configuration-hook*))
  8.7529-
  8.7530-  (register-image-dump-hook 'clear-configuration)
  8.7531-
  8.7532-  (defun upgrade-configuration ()
  8.7533-    "If a previous version of ASDF failed to read some configuration, try again now."
  8.7534-    (when *ignored-configuration-form*
  8.7535-      (clear-configuration)
  8.7536-      (setf *ignored-configuration-form* nil)))
  8.7537-
  8.7538-
  8.7539-  (defun get-folder-path (folder)
  8.7540-    "Semi-portable implementation of a subset of LispWorks' sys:get-folder-path,
  8.7541-this function tries to locate the Windows FOLDER for one of
  8.7542-:LOCAL-APPDATA, :APPDATA or :COMMON-APPDATA.
  8.7543-     Returns NIL when the folder is not defined (e.g., not on Windows)."
  8.7544-    (or #+(and lispworks os-windows) (sys:get-folder-path folder)
  8.7545-        ;; read-windows-registry HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders\AppData
  8.7546-        (ecase folder
  8.7547-          (:local-appdata (or (getenv-absolute-directory "LOCALAPPDATA")
  8.7548-                              (subpathname* (get-folder-path :appdata) "Local")))
  8.7549-          (:appdata (getenv-absolute-directory "APPDATA"))
  8.7550-          (:common-appdata (or (getenv-absolute-directory "ALLUSERSAPPDATA")
  8.7551-                               (subpathname* (getenv-absolute-directory "ALLUSERSPROFILE") "Application Data/"))))))
  8.7552-
  8.7553-
  8.7554-  ;; Support for the XDG Base Directory Specification
  8.7555-  (defun xdg-data-home (&rest more)
  8.7556-    "Returns an absolute pathname for the directory containing user-specific data files.
  8.7557-MORE may contain specifications for a subpath relative to this directory: a
  8.7558-subpathname specification and keyword arguments as per RESOLVE-LOCATION \(see
  8.7559-also \"Configuration DSL\"\) in the ASDF manual."
  8.7560-    (resolve-absolute-location
  8.7561-     `(,(or (getenv-absolute-directory "XDG_DATA_HOME")
  8.7562-            (os-cond
  8.7563-             ((os-windows-p) (get-folder-path :local-appdata))
  8.7564-             (t (subpathname (user-homedir-pathname) ".local/share/"))))
  8.7565-       ,more)))
  8.7566-
  8.7567-  (defun xdg-config-home (&rest more)
  8.7568-    "Returns a pathname for the directory containing user-specific configuration files.
  8.7569-MORE may contain specifications for a subpath relative to this directory: a
  8.7570-subpathname specification and keyword arguments as per RESOLVE-LOCATION \(see
  8.7571-also \"Configuration DSL\"\) in the ASDF manual."
  8.7572-    (resolve-absolute-location
  8.7573-     `(,(or (getenv-absolute-directory "XDG_CONFIG_HOME")
  8.7574-            (os-cond
  8.7575-             ((os-windows-p) (xdg-data-home "config/"))
  8.7576-             (t (subpathname (user-homedir-pathname) ".config/"))))
  8.7577-       ,more)))
  8.7578-
  8.7579-  (defun xdg-data-dirs (&rest more)
  8.7580-    "The preference-ordered set of additional paths to search for data files.
  8.7581-Returns a list of absolute directory pathnames.
  8.7582-MORE may contain specifications for a subpath relative to these directories: a
  8.7583-subpathname specification and keyword arguments as per RESOLVE-LOCATION \(see
  8.7584-also \"Configuration DSL\"\) in the ASDF manual."
  8.7585-    (mapcar #'(lambda (d) (resolve-location `(,d ,more)))
  8.7586-            (or (remove nil (getenv-absolute-directories "XDG_DATA_DIRS"))
  8.7587-                (os-cond
  8.7588-                 ((os-windows-p) (mapcar 'get-folder-path '(:appdata :common-appdata)))
  8.7589-                 ;; macOS' separate read-only system volume means that the contents
  8.7590-                 ;; of /usr/share are frozen by Apple. Unlike when running natively
  8.7591-                 ;; on macOS, Genera must access the filesystem through NFS. Attempting
  8.7592-                 ;; to export either the root (/) or /usr/share simply doesn't work.
  8.7593-                 ;; (Genera will go into an infinite loop trying to access those mounts.)
  8.7594-                 ;; So, when running Genera on macOS, only search /usr/local/share.
  8.7595-                 ((os-genera-p)
  8.7596-                  #+Genera (sys:system-case
  8.7597-                            (darwin-vlm (mapcar 'parse-unix-namestring '("/usr/local/share/")))
  8.7598-                            (otherwise (mapcar 'parse-unix-namestring '("/usr/local/share/" "/usr/share/")))))
  8.7599-                 (t (mapcar 'parse-unix-namestring '("/usr/local/share/" "/usr/share/")))))))
  8.7600-
  8.7601-  (defun xdg-config-dirs (&rest more)
  8.7602-    "The preference-ordered set of additional base paths to search for configuration files.
  8.7603-Returns a list of absolute directory pathnames.
  8.7604-MORE may contain specifications for a subpath relative to these directories:
  8.7605-subpathname specification and keyword arguments as per RESOLVE-LOCATION \(see
  8.7606-also \"Configuration DSL\"\) in the ASDF manual."
  8.7607-    (mapcar #'(lambda (d) (resolve-location `(,d ,more)))
  8.7608-            (or (remove nil (getenv-absolute-directories "XDG_CONFIG_DIRS"))
  8.7609-                (os-cond
  8.7610-                 ((os-windows-p) (xdg-data-dirs "config/"))
  8.7611-                 (t (mapcar 'parse-unix-namestring '("/etc/xdg/")))))))
  8.7612-
  8.7613-  (defun xdg-cache-home (&rest more)
  8.7614-    "The base directory relative to which user specific non-essential data files should be stored.
  8.7615-Returns an absolute directory pathname.
  8.7616-MORE may contain specifications for a subpath relative to this directory: a
  8.7617-subpathname specification and keyword arguments as per RESOLVE-LOCATION \(see
  8.7618-also \"Configuration DSL\"\) in the ASDF manual."
  8.7619-    (resolve-absolute-location
  8.7620-     `(,(or (getenv-absolute-directory "XDG_CACHE_HOME")
  8.7621-            (os-cond
  8.7622-             ((os-windows-p) (xdg-data-home "cache/"))
  8.7623-             (t (subpathname* (user-homedir-pathname) ".cache/"))))
  8.7624-       ,more)))
  8.7625-
  8.7626-  (defun xdg-runtime-dir (&rest more)
  8.7627-    "Pathname for user-specific non-essential runtime files and other file objects,
  8.7628-such as sockets, named pipes, etc.
  8.7629-Returns an absolute directory pathname.
  8.7630-MORE may contain specifications for a subpath relative to this directory: a
  8.7631-subpathname specification and keyword arguments as per RESOLVE-LOCATION \(see
  8.7632-also \"Configuration DSL\"\) in the ASDF manual."
  8.7633-    ;; The XDG spec says that if not provided by the login system, the application should
  8.7634-    ;; issue a warning and provide a replacement. UIOP is not equipped to do that and returns NIL.
  8.7635-    (resolve-absolute-location `(,(getenv-absolute-directory "XDG_RUNTIME_DIR") ,more)))
  8.7636-
  8.7637-  ;;; NOTE: modified the docstring because "system user configuration
  8.7638-  ;;; directories" seems self-contradictory. I'm not sure my wording is right.
  8.7639-  (defun system-config-pathnames (&rest more)
  8.7640-    "Return a list of directories where are stored the system's default user configuration information.
  8.7641-MORE may contain specifications for a subpath relative to these directories: a
  8.7642-subpathname specification and keyword arguments as per RESOLVE-LOCATION \(see
  8.7643-also \"Configuration DSL\"\) in the ASDF manual."
  8.7644-    (declare (ignorable more))
  8.7645-    (os-cond
  8.7646-     ((os-unix-p) (list (resolve-absolute-location `(,(parse-unix-namestring "/etc/") ,more))))))
  8.7647-
  8.7648-  (defun filter-pathname-set (dirs)
  8.7649-    "Parse strings as unix namestrings and remove duplicates and non absolute-pathnames in a list."
  8.7650-    (remove-duplicates (remove-if-not #'absolute-pathname-p dirs) :from-end t :test 'equal))
  8.7651-
  8.7652-  (defun xdg-data-pathnames (&rest more)
  8.7653-    "Return a list of absolute pathnames for application data directories.  With APP,
  8.7654-returns directory for data for that application, without APP, returns the set of directories
  8.7655-for storing all application configurations.
  8.7656-MORE may contain specifications for a subpath relative to these directories: a
  8.7657-subpathname specification and keyword arguments as per RESOLVE-LOCATION \(see
  8.7658-also \"Configuration DSL\"\) in the ASDF manual."
  8.7659-    (filter-pathname-set
  8.7660-     `(,(xdg-data-home more)
  8.7661-       ,@(xdg-data-dirs more))))
  8.7662-
  8.7663-  (defun xdg-config-pathnames (&rest more)
  8.7664-    "Return a list of pathnames for application configuration.
  8.7665-MORE may contain specifications for a subpath relative to these directories: a
  8.7666-subpathname specification and keyword arguments as per RESOLVE-LOCATION \(see
  8.7667-also \"Configuration DSL\"\) in the ASDF manual."
  8.7668-    (filter-pathname-set
  8.7669-     `(,(xdg-config-home more)
  8.7670-       ,@(xdg-config-dirs more))))
  8.7671-
  8.7672-  (defun find-preferred-file (files &key (direction :input))
  8.7673-    "Find first file in the list of FILES that exists (for direction :input or :probe)
  8.7674-or just the first one (for direction :output or :io).
  8.7675-    Note that when we say \"file\" here, the files in question may be directories."
  8.7676-    (find-if (ecase direction ((:probe :input) 'probe-file*) ((:output :io) 'identity)) files))
  8.7677-
  8.7678-  (defun xdg-data-pathname (&optional more (direction :input))
  8.7679-    (find-preferred-file (xdg-data-pathnames more) :direction direction))
  8.7680-
  8.7681-  (defun xdg-config-pathname (&optional more (direction :input))
  8.7682-    (find-preferred-file (xdg-config-pathnames more) :direction direction))
  8.7683-
  8.7684-  (defun compute-user-cache ()
  8.7685-    "Compute (and return) the location of the default user-cache for translate-output
  8.7686-objects. Side-effects for cached file location computation."
  8.7687-    (setf *user-cache* (xdg-cache-home "common-lisp" :implementation)))
  8.7688-  (register-image-restore-hook 'compute-user-cache)
  8.7689-
  8.7690-  (defun uiop-directory ()
  8.7691-    "Try to locate the UIOP source directory at runtime"
  8.7692-    (labels ((pf (x) (ignore-errors (probe-file* x)))
  8.7693-             (sub (x y) (pf (subpathname x y)))
  8.7694-             (ssd (x) (ignore-errors (symbol-call :asdf :system-source-directory x))))
  8.7695-      ;; NB: conspicuously *not* including searches based on #.(current-lisp-pathname)
  8.7696-      (or
  8.7697-       ;; Look under uiop if available as source override, under asdf if avaiable as source
  8.7698-       (ssd "uiop")
  8.7699-       (sub (ssd "asdf") "uiop/")
  8.7700-       ;; Look in recommended path for user-visible source installation
  8.7701-       (sub (user-homedir-pathname) "common-lisp/asdf/uiop/")
  8.7702-       ;; Look in XDG paths under known package names for user-invisible source installation
  8.7703-       (xdg-data-pathname "common-lisp/source/asdf/uiop/")
  8.7704-       (xdg-data-pathname "common-lisp/source/cl-asdf/uiop/") ; traditional Debian location
  8.7705-       ;; The last one below is useful for Fare, primary (sole?) known user
  8.7706-       (sub (user-homedir-pathname) "cl/asdf/uiop/")
  8.7707-       (cerror "Configure source registry to include UIOP source directory and retry."
  8.7708-               "Unable to find UIOP directory")
  8.7709-       (uiop-directory)))))
  8.7710-;;; -------------------------------------------------------------------------
  8.7711-;;; Hacks for backward-compatibility with older versions of UIOP
  8.7712-
  8.7713-(uiop/package:define-package :uiop/backward-driver
  8.7714-  (:recycle :uiop/backward-driver :asdf/backward-driver :uiop)
  8.7715-  (:use :uiop/common-lisp :uiop/package :uiop/utility :uiop/version
  8.7716-   :uiop/pathname :uiop/stream :uiop/os :uiop/image
  8.7717-   :uiop/run-program :uiop/lisp-build :uiop/configuration)
  8.7718-  (:export
  8.7719-   #:coerce-pathname
  8.7720-   #:user-configuration-directories #:system-configuration-directories
  8.7721-   #:in-first-directory #:in-user-configuration-directory #:in-system-configuration-directory
  8.7722-   #:version-compatible-p))
  8.7723-(in-package :uiop/backward-driver)
  8.7724-
  8.7725-(eval-when (:compile-toplevel :load-toplevel :execute)
  8.7726-(with-deprecation ((version-deprecation *uiop-version* :style-warning "3.2" :warning "3.4"))
  8.7727-  ;; Backward compatibility with ASDF 2.000 to 2.26
  8.7728-
  8.7729-  ;; For backward-compatibility only, for people using internals
  8.7730-  ;; Reported users in quicklisp 2015-11: hu.dwim.asdf (removed in next release)
  8.7731-  ;; Will be removed after 2015-12.
  8.7732-  (defun coerce-pathname (name &key type defaults)
  8.7733-    "DEPRECATED. Please use UIOP:PARSE-UNIX-NAMESTRING instead."
  8.7734-    (parse-unix-namestring name :type type :defaults defaults))
  8.7735-
  8.7736-  ;; Backward compatibility for ASDF 2.27 to 3.1.4
  8.7737-  (defun user-configuration-directories ()
  8.7738-    "Return the current user's list of user configuration directories
  8.7739-for configuring common-lisp.
  8.7740-DEPRECATED. Use UIOP:XDG-CONFIG-PATHNAMES instead."
  8.7741-    (xdg-config-pathnames "common-lisp"))
  8.7742-  (defun system-configuration-directories ()
  8.7743-    "Return the list of system configuration directories for common-lisp.
  8.7744-DEPRECATED. Use UIOP:SYSTEM-CONFIG-PATHNAMES (with argument \"common-lisp\"),
  8.7745-instead."
  8.7746-    (system-config-pathnames "common-lisp"))
  8.7747-  (defun in-first-directory (dirs x &key (direction :input))
  8.7748-    "Finds the first appropriate file named X in the list of DIRS for I/O
  8.7749-in DIRECTION \(which may be :INPUT, :OUTPUT, :IO, or :PROBE).
  8.7750-If direction is :INPUT or :PROBE, will return the first extant file named
  8.7751-X in one of the DIRS.
  8.7752-If direction is :OUTPUT or :IO, will simply return the file named X in the
  8.7753-first element of DIRS that exists. DEPRECATED."
  8.7754-    (find-preferred-file
  8.7755-     (mapcar #'(lambda (dir) (subpathname (ensure-directory-pathname dir) x)) dirs)
  8.7756-     :direction direction))
  8.7757-  (defun in-user-configuration-directory (x &key (direction :input))
  8.7758-    "Return the file named X in the user configuration directory for common-lisp.
  8.7759-DEPRECATED."
  8.7760-    (xdg-config-pathname `("common-lisp" ,x) direction))
  8.7761-  (defun in-system-configuration-directory (x &key (direction :input))
  8.7762-    "Return the pathname for the file named X under the system configuration directory
  8.7763-for common-lisp. DEPRECATED."
  8.7764-    (find-preferred-file (system-config-pathnames "common-lisp" x) :direction direction))
  8.7765-
  8.7766-
  8.7767-  ;; Backward compatibility with ASDF 1 to ASDF 2.32
  8.7768-
  8.7769-  (defun version-compatible-p (provided-version required-version)
  8.7770-    "Is the provided version a compatible substitution for the required-version?
  8.7771-If major versions differ, it's not compatible.
  8.7772-If they are equal, then any later version is compatible,
  8.7773-with later being determined by a lexicographical comparison of minor numbers.
  8.7774-DEPRECATED."
  8.7775-    (let ((x (parse-version provided-version nil))
  8.7776-          (y (parse-version required-version nil)))
  8.7777-      (and x y (= (car x) (car y)) (lexicographic<= '< (cdr y) (cdr x)))))))
  8.7778-
  8.7779-;;;; ---------------------------------------------------------------------------
  8.7780-;;;; Re-export all the functionality in UIOP
  8.7781-
  8.7782-(uiop/package:define-package :uiop/driver
  8.7783-  (:nicknames :uiop ;; Official name we recommend should be used for all references to uiop symbols.
  8.7784-              :asdf/driver) ;; DO NOT USE, a deprecated name, not supported anymore.
  8.7785-  ;; We should remove the name :asdf/driver at some point,
  8.7786-  ;; but not until it has been eradicated from Quicklisp for a year or two.
  8.7787-  ;; The last known user was cffi (PR merged in May 2020).
  8.7788-  (:use :uiop/common-lisp)
  8.7789-  ;; NB: We are not reexporting uiop/common-lisp
  8.7790-  ;; which include all of CL with compatibility modifications on select platforms,
  8.7791-  ;; because that would cause potential conflicts for packages that
  8.7792-  ;; might want to :use (:cl :uiop) or :use (:closer-common-lisp :uiop), etc.
  8.7793-  (:use-reexport
  8.7794-   :uiop/package* :uiop/utility :uiop/version
  8.7795-   :uiop/os :uiop/pathname :uiop/filesystem :uiop/stream :uiop/image
  8.7796-   :uiop/launch-program :uiop/run-program
  8.7797-   :uiop/lisp-build :uiop/configuration :uiop/backward-driver))
  8.7798-
  8.7799-;; Provide both lowercase and uppercase, to satisfy more implementations.
  8.7800-(provide "uiop") (provide "UIOP")
  8.7801-;;;; -------------------------------------------------------------------------
  8.7802-;;;; Handle upgrade as forward- and backward-compatibly as possible
  8.7803-;; See https://bugs.launchpad.net/asdf/+bug/485687
  8.7804-
  8.7805-(uiop/package:define-package :asdf/upgrade
  8.7806-  (:recycle :asdf/upgrade :asdf)
  8.7807-  (:use :uiop/common-lisp :uiop)
  8.7808-  (:export
  8.7809-   #:asdf-version #:*previous-asdf-versions* #:*asdf-version*
  8.7810-   #:asdf-message #:*verbose-out*
  8.7811-   #:upgrading-p #:when-upgrading #:upgrade-asdf #:defparameter*
  8.7812-   #:*post-upgrade-cleanup-hook* #:cleanup-upgraded-asdf
  8.7813-   ;; There will be no symbol left behind!
  8.7814-   #:with-asdf-deprecation
  8.7815-   #:intern*)
  8.7816-  (:import-from :uiop/package #:intern* #:find-symbol*))
  8.7817-(in-package :asdf/upgrade)
  8.7818-
  8.7819-;;; Special magic to detect if this is an upgrade
  8.7820-
  8.7821-(with-upgradability ()
  8.7822-  (defun asdf-version ()
  8.7823-    "Exported interface to the version of ASDF currently installed. A string.
  8.7824-You can compare this string with e.g.: (ASDF:VERSION-SATISFIES (ASDF:ASDF-VERSION) \"3.4.5.67\")."
  8.7825-    (when (find-package :asdf)
  8.7826-      (or (symbol-value (find-symbol (string :*asdf-version*) :asdf))
  8.7827-          (let* ((revsym (find-symbol (string :*asdf-revision*) :asdf))
  8.7828-                 (rev (and revsym (boundp revsym) (symbol-value revsym))))
  8.7829-            (etypecase rev
  8.7830-              (string rev)
  8.7831-              (cons (format nil "~{~D~^.~}" rev))
  8.7832-              (null "1.0"))))))
  8.7833-  ;; This (private) variable contains a list of versions of previously loaded variants of ASDF,
  8.7834-  ;; from which ASDF was upgraded.
  8.7835-  ;; Important: define *p-a-v* /before/ *a-v* so that they initialize correctly.
  8.7836-  (defvar *previous-asdf-versions*
  8.7837-    (let ((previous (asdf-version)))
  8.7838-      (when previous
  8.7839-        ;; Punt on upgrade from ASDF1 or ASDF2, by renaming (or deleting) the package.
  8.7840-        (when (version< previous "2.27") ;; 2.27 is the first to have the :asdf3 feature.
  8.7841-          (let ((away (format nil "~A-~A" :asdf previous)))
  8.7842-            (rename-package :asdf away)
  8.7843-            (when *load-verbose*
  8.7844-              (format t "~&; Renamed old ~A package away to ~A~%" :asdf away))))
  8.7845-        (list previous))))
  8.7846-  ;; This public variable will be bound shortly to the currently loaded version of ASDF.
  8.7847-  (defvar *asdf-version* nil)
  8.7848-  ;; We need to clear systems from versions older than the one in this (private) parameter.
  8.7849-  ;; The latest incompatible defclass is 2.32.13 renaming a slot in component,
  8.7850-  ;; or 3.2.0.2 for CCL (incompatibly changing some superclasses).
  8.7851-  ;; the latest incompatible gf change is in 3.1.7.20 (see redefined-functions below).
  8.7852-  (defparameter *oldest-forward-compatible-asdf-version* "3.2.0.2")
  8.7853-  ;; Semi-private variable: a designator for a stream on which to output ASDF progress messages
  8.7854-  (defvar *verbose-out* nil)
  8.7855-  ;; Private function by which ASDF outputs progress messages and warning messages:
  8.7856-  (defun asdf-message (format-string &rest format-args)
  8.7857-    (when *verbose-out* (apply 'format *verbose-out* format-string format-args)))
  8.7858-  ;; Private hook for functions to run after ASDF has upgraded itself from an older variant:
  8.7859-  (defvar *post-upgrade-cleanup-hook* ())
  8.7860-  ;; Private variable for post upgrade cleanup to communicate if an upgrade has
  8.7861-  ;; actually occured.
  8.7862-  (defvar *asdf-upgraded-p*)
  8.7863-  ;; Private function to detect whether the current upgrade counts as an incompatible
  8.7864-  ;; data schema upgrade implying the need to drop data.
  8.7865-  (defun upgrading-p (&optional (oldest-compatible-version *oldest-forward-compatible-asdf-version*))
  8.7866-    (and *previous-asdf-versions*
  8.7867-         (version< (first *previous-asdf-versions*) oldest-compatible-version)))
  8.7868-  ;; Private variant of defparameter that works in presence of incompatible upgrades:
  8.7869-  ;; behaves like defvar in a compatible upgrade (e.g. reloading system after simple code change),
  8.7870-  ;; but behaves like defparameter if in presence of an incompatible upgrade.
  8.7871-  (defmacro defparameter* (var value &optional docstring (version *oldest-forward-compatible-asdf-version*))
  8.7872-    (let* ((name (string-trim "*" var))
  8.7873-           (valfun (intern (format nil "%~A-~A-~A" :compute name :value))))
  8.7874-      `(progn
  8.7875-         (defun ,valfun () ,value)
  8.7876-         (defvar ,var (,valfun) ,@(ensure-list docstring))
  8.7877-         (when (upgrading-p ,version)
  8.7878-           (setf ,var (,valfun))))))
  8.7879-  ;; Private macro to declare sections of code that are only compiled and run when upgrading.
  8.7880-  ;; The use of eval portably ensures that the code will not have adverse compile-time side-effects,
  8.7881-  ;; whereas the use of handler-bind portably ensures that it will not issue warnings when it runs.
  8.7882-  (defmacro when-upgrading ((&key (version *oldest-forward-compatible-asdf-version*)
  8.7883-                               (upgrading-p `(upgrading-p ,version)) when) &body body)
  8.7884-    "A wrapper macro for code that should only be run when upgrading a
  8.7885-previously-loaded version of ASDF."
  8.7886-    `(with-upgradability ()
  8.7887-       (when (and ,upgrading-p ,@(when when `(,when)))
  8.7888-         (handler-bind ((style-warning #'muffle-warning))
  8.7889-           (eval '(progn ,@body))))))
  8.7890-  ;; Only now can we safely update the version.
  8.7891-  (let* (;; For bug reporting sanity, please always bump this version when you modify this file.
  8.7892-         ;; Please also modify asdf.asd to reflect this change. make bump-version v=3.4.5.67.8
  8.7893-         ;; can help you do these changes in synch (look at the source for documentation).
  8.7894-         ;; Relying on its automation, the version is now redundantly present on top of asdf.lisp.
  8.7895-         ;; "3.4" would be the general branch for major version 3, minor version 4.
  8.7896-         ;; "3.4.5" would be an official release in the 3.4 branch.
  8.7897-         ;; "3.4.5.67" would be a development version in the official branch, on top of 3.4.5.
  8.7898-         ;; "3.4.5.0.8" would be your eighth local modification of official release 3.4.5
  8.7899-         ;; "3.4.5.67.8" would be your eighth local modification of development version 3.4.5.67
  8.7900-         (asdf-version "3.3.6")
  8.7901-         (existing-version (asdf-version)))
  8.7902-    (setf *asdf-version* asdf-version)
  8.7903-    (when (and existing-version (not (equal asdf-version existing-version)))
  8.7904-      (push existing-version *previous-asdf-versions*)
  8.7905-      (when (or *verbose-out* *load-verbose*)
  8.7906-        (format (or *verbose-out* *trace-output*)
  8.7907-                (compatfmt "~&~@<; ~@;Upgrading ASDF ~@[from version ~A ~]to version ~A~@:>~%")
  8.7908-                existing-version asdf-version)))))
  8.7909-
  8.7910-;;; Upon upgrade, specially frob some functions and classes that are being incompatibly redefined
  8.7911-(when-upgrading ()
  8.7912-  (let* ((previous-version (first *previous-asdf-versions*))
  8.7913-         (redefined-functions ;; List of functions that changed incompatibly since 2.27:
  8.7914-          ;; gf signature changed, defun that became a generic function (but not way around),
  8.7915-          ;; method removed that will mess up with new ones
  8.7916-          ;; (especially :around :before :after, more specific or call-next-method'ed method)
  8.7917-          ;; and/or semantics otherwise modified. Oops.
  8.7918-          ;; NB: it's too late to do anything about functions in UIOP!
  8.7919-          ;; If you introduce some critical incompatibility there, you MUST change the function name.
  8.7920-          ;; Note that we don't need do anything about functions that changed incompatibly
  8.7921-          ;; from ASDF 2.26 or earlier: we wholly punt on the entire ASDF package in such an upgrade.
  8.7922-          ;; Also, the strong constraints apply most importantly for functions called from
  8.7923-          ;; the continuation of compiling or loading some of the code in ASDF or UIOP.
  8.7924-          ;; See discussion at https://gitlab.common-lisp.net/asdf/asdf/merge_requests/36
  8.7925-          ;; and at https://gitlab.common-lisp.net/asdf/asdf/-/merge_requests/141
  8.7926-          `(,@(when (version< previous-version "2.31") '(#:normalize-version)) ;; pathname became &key
  8.7927-            ,@(when (version< previous-version "3.1.2") '(#:component-depends-on #:input-files)) ;; crucial methods *removed* before 3.1.2
  8.7928-            ,@(when (version< previous-version "3.1.7.20") '(#:find-component)))) ;; added &key registered
  8.7929-         (redefined-classes
  8.7930-          ;; with the old ASDF during upgrade, and many implementations bork
  8.7931-          (when (or #+(or clozure mkcl) t)
  8.7932-            '((#:compile-concatenated-source-op (#:operation) ())
  8.7933-              (#:compile-bundle-op (#:operation) ())
  8.7934-              (#:concatenate-source-op (#:operation) ())
  8.7935-              (#:dll-op (#:operation) ())
  8.7936-              (#:lib-op (#:operation) ())
  8.7937-              (#:monolithic-compile-bundle-op (#:operation) ())
  8.7938-              (#:monolithic-concatenate-source-op (#:operation) ())))))
  8.7939-    (loop :for name :in redefined-functions
  8.7940-      :for sym = (find-symbol* name :asdf nil)
  8.7941-      :do (when sym (fmakunbound sym)))
  8.7942-    (labels ((asym (x) (multiple-value-bind (s p)
  8.7943-                           (if (consp x) (values (car x) (cadr x)) (values x :asdf))
  8.7944-                         (find-symbol* s p nil)))
  8.7945-             (asyms (l) (mapcar #'asym l)))
  8.7946-      (loop :for (name superclasses slots) :in redefined-classes
  8.7947-            :for sym = (find-symbol* name :asdf nil)
  8.7948-            :when (and sym (find-class sym))
  8.7949-              :do #+ccl (eval `(defclass ,sym ,(asyms superclasses) ,(asyms slots)))
  8.7950-                  #-ccl (setf (find-class sym) nil))))) ;; mkcl
  8.7951-
  8.7952-;;; Self-upgrade functions
  8.7953-(with-upgradability ()
  8.7954-  ;; This private function is called at the end of asdf/footer and ensures that,
  8.7955-  ;; *if* this loading of ASDF was an upgrade, then all registered cleanup functions will be called.
  8.7956-  (defun cleanup-upgraded-asdf (&optional (old-version (first *previous-asdf-versions*)))
  8.7957-    (let ((new-version (asdf-version)))
  8.7958-      (unless (equal old-version new-version)
  8.7959-        (push new-version *previous-asdf-versions*)
  8.7960-        (when (boundp '*asdf-upgraded-p*)
  8.7961-          (setf *asdf-upgraded-p* t))
  8.7962-        (when old-version
  8.7963-          (if (version<= new-version old-version)
  8.7964-              (error (compatfmt "~&~@<; ~@;Downgraded ASDF from version ~A to version ~A~@:>~%")
  8.7965-                     old-version new-version)
  8.7966-              (asdf-message (compatfmt "~&~@<; ~@;Upgraded ASDF from version ~A to version ~A~@:>~%")
  8.7967-                            old-version new-version))
  8.7968-          ;; In case the previous version was too old to be forward-compatible, clear systems.
  8.7969-          ;; TODO: if needed, we may have to define a separate hook to run
  8.7970-          ;; in case of forward-compatible upgrade.
  8.7971-          ;; Or to move the tests forward-compatibility test inside each hook function?
  8.7972-          (unless (version<= *oldest-forward-compatible-asdf-version* old-version)
  8.7973-            (call-functions (reverse *post-upgrade-cleanup-hook*)))
  8.7974-          t))))
  8.7975-
  8.7976-  (defun upgrade-asdf ()
  8.7977-    "Try to upgrade of ASDF. If a different version was used, return T.
  8.7978-   We need do that before we operate on anything that may possibly depend on ASDF."
  8.7979-    (let ((*load-print* nil)
  8.7980-          (*compile-print* nil)
  8.7981-          (*asdf-upgraded-p* nil))
  8.7982-      (handler-bind (((or style-warning) #'muffle-warning))
  8.7983-        (symbol-call :asdf :load-system :asdf :verbose nil))
  8.7984-      *asdf-upgraded-p*))
  8.7985-
  8.7986-  (defmacro with-asdf-deprecation ((&rest keys &key &allow-other-keys) &body body)
  8.7987-    `(with-upgradability ()
  8.7988-       (with-deprecation ((version-deprecation *asdf-version* ,@keys))
  8.7989-         ,@body))))
  8.7990-;;;; -------------------------------------------------------------------------
  8.7991-;;;; Session
  8.7992-
  8.7993-(uiop/package:define-package :asdf/session
  8.7994-  (:recycle :asdf/session :asdf/cache :asdf/component
  8.7995-            :asdf/action :asdf/find-system :asdf/plan :asdf)
  8.7996-  (:use :uiop/common-lisp :uiop :asdf/upgrade)
  8.7997-  (:export
  8.7998-   #:get-file-stamp #:compute-file-stamp #:register-file-stamp
  8.7999-   #:asdf-cache #:set-asdf-cache-entry #:unset-asdf-cache-entry #:consult-asdf-cache
  8.8000-   #:do-asdf-cache #:normalize-namestring
  8.8001-   #:call-with-asdf-session #:with-asdf-session
  8.8002-   #:*asdf-session* #:*asdf-session-class* #:session #:toplevel-asdf-session
  8.8003-   #:session-cache #:forcing #:asdf-upgraded-p
  8.8004-   #:visited-actions #:visiting-action-set #:visiting-action-list
  8.8005-   #:total-action-count #:planned-action-count #:planned-output-action-count
  8.8006-   #:clear-configuration-and-retry #:retry
  8.8007-   #:operate-level
  8.8008-   ;; conditions
  8.8009-   #:system-definition-error ;; top level, moved here because this is the earliest place for it.
  8.8010-   #:formatted-system-definition-error #:format-control #:format-arguments #:sysdef-error))
  8.8011-(in-package :asdf/session)
  8.8012-
  8.8013-
  8.8014-(with-upgradability ()
  8.8015-  ;; The session variable.
  8.8016-  ;; NIL when outside a session.
  8.8017-  (defvar *asdf-session* nil)
  8.8018-  (defparameter* *asdf-session-class* 'session
  8.8019-    "The default class for sessions")
  8.8020-
  8.8021-  (defclass session ()
  8.8022-    (;; The ASDF session cache is used to memoize some computations.
  8.8023-     ;; It is instrumental in achieving:
  8.8024-     ;; * Consistency in the view of the world relied on by ASDF within a given session.
  8.8025-     ;;   Inconsistencies in file stamps, system definitions, etc., could cause infinite loops
  8.8026-     ;;   (a.k.a. stack overflows) and other erratic behavior.
  8.8027-     ;; * Speed and reliability of ASDF, with fewer side-effects from access to the filesystem, and
  8.8028-     ;;   no expensive recomputations of transitive dependencies for input-files or output-files.
  8.8029-     ;; * Testability of ASDF with the ability to fake timestamps without actually touching files.
  8.8030-     (ancestor
  8.8031-      :initform nil :initarg :ancestor :reader session-ancestor
  8.8032-      :documentation "Top level session that this is part of")
  8.8033-     (session-cache
  8.8034-      :initform (make-hash-table :test 'equal) :initarg :session-cache :reader session-cache
  8.8035-      :documentation "Memoize expensive computations")
  8.8036-     (operate-level
  8.8037-      :initform 0 :initarg :operate-level :accessor session-operate-level
  8.8038-      :documentation "Number of nested calls to operate we're under (for toplevel session only)")
  8.8039-     ;; shouldn't the below be superseded by the session-wide caching of action-status
  8.8040-     ;; for (load-op "asdf") ?
  8.8041-     (asdf-upgraded-p
  8.8042-      :initform nil :initarg :asdf-upgraded-p :accessor asdf-upgraded-p
  8.8043-      :documentation "Was ASDF already upgraded in this session - only valid for toplevel-asdf-session.")
  8.8044-     (forcing
  8.8045-      :initform nil :initarg :forcing :accessor forcing
  8.8046-      :documentation "Forcing parameters for the session")
  8.8047-     ;; Table that to actions already visited while walking the dependencies associates status
  8.8048-     (visited-actions :initform (make-hash-table :test 'equal) :accessor visited-actions)
  8.8049-     ;; Actions that depend on those being currently walked through, to detect circularities
  8.8050-     (visiting-action-set ;; as a set
  8.8051-      :initform (make-hash-table :test 'equal) :accessor visiting-action-set)
  8.8052-     (visiting-action-list :initform () :accessor visiting-action-list) ;; as a list
  8.8053-     ;; Counts of total actions in plan
  8.8054-     (total-action-count :initform 0 :accessor total-action-count)
  8.8055-     ;; Count of actions that need to be performed
  8.8056-     (planned-action-count :initform 0 :accessor planned-action-count)
  8.8057-     ;; Count of actions that need to be performed that have a non-empty list of output-files.
  8.8058-     (planned-output-action-count :initform 0 :accessor planned-output-action-count))
  8.8059-    (:documentation "An ASDF session with a cache to memoize some computations"))
  8.8060-
  8.8061-  (defun toplevel-asdf-session ()
  8.8062-    (when *asdf-session* (or (session-ancestor *asdf-session*) *asdf-session*)))
  8.8063-
  8.8064-  (defun operate-level ()
  8.8065-    (session-operate-level (toplevel-asdf-session)))
  8.8066-
  8.8067-  (defun (setf operate-level) (new-level)
  8.8068-    (setf (session-operate-level (toplevel-asdf-session)) new-level))
  8.8069-
  8.8070-  (defun asdf-cache ()
  8.8071-    (session-cache *asdf-session*))
  8.8072-
  8.8073-  ;; Set a session cache entry for KEY to a list of values VALUE-LIST, when inside a session.
  8.8074-  ;; Return those values.
  8.8075-  (defun set-asdf-cache-entry (key value-list)
  8.8076-    (values-list (if *asdf-session*
  8.8077-                     (setf (gethash key (asdf-cache)) value-list)
  8.8078-                     value-list)))
  8.8079-
  8.8080-  ;; Unset the session cache entry for KEY, when inside a session.
  8.8081-  (defun unset-asdf-cache-entry (key)
  8.8082-    (when *asdf-session*
  8.8083-      (remhash key (session-cache *asdf-session*))))
  8.8084-
  8.8085-  ;; Consult the session cache entry for KEY if present and in a session;
  8.8086-  ;; if not present, compute it by calling the THUNK,
  8.8087-  ;; and set the session cache entry accordingly, if in a session.
  8.8088-  ;; Return the values from the cache and/or the thunk computation.
  8.8089-  (defun consult-asdf-cache (key &optional thunk)
  8.8090-    (if *asdf-session*
  8.8091-        (multiple-value-bind (results foundp) (gethash key (session-cache *asdf-session*))
  8.8092-          (if foundp
  8.8093-              (values-list results)
  8.8094-              (set-asdf-cache-entry key (multiple-value-list (call-function thunk)))))
  8.8095-        (call-function thunk)))
  8.8096-
  8.8097-  ;; Syntactic sugar for consult-asdf-cache
  8.8098-  (defmacro do-asdf-cache (key &body body)
  8.8099-    `(consult-asdf-cache ,key #'(lambda () ,@body)))
  8.8100-
  8.8101-  ;; Compute inside a ASDF session with a cache.
  8.8102-  ;; First, make sure an ASDF session is underway, by binding the session cache variable
  8.8103-  ;; to a new hash-table if it's currently null (or even if it isn't, if OVERRIDE is true).
  8.8104-  ;; Second, if a new session was started, establish restarts for retrying the overall computation.
  8.8105-  ;; Finally, consult the cache if a KEY was specified with the THUNK as a fallback when the cache
  8.8106-  ;; entry isn't found, or just call the THUNK if no KEY was specified.
  8.8107-  (defun call-with-asdf-session (thunk &key override key override-cache override-forcing)
  8.8108-    (let ((fun (if key #'(lambda () (consult-asdf-cache key thunk)) thunk)))
  8.8109-      (if (and (not override) *asdf-session*)
  8.8110-          (funcall fun)
  8.8111-          (loop
  8.8112-            (restart-case
  8.8113-                (let ((*asdf-session*
  8.8114-                       (apply 'make-instance *asdf-session-class*
  8.8115-                              (when *asdf-session*
  8.8116-                                `(:ancestor ,(toplevel-asdf-session)
  8.8117-                                  ,@(unless override-forcing
  8.8118-                                      `(:forcing ,(forcing *asdf-session*)))
  8.8119-                                  ,@(unless override-cache
  8.8120-                                      `(:session-cache ,(session-cache *asdf-session*))))))))
  8.8121-                  (return (funcall fun)))
  8.8122-              (retry ()
  8.8123-                :report (lambda (s)
  8.8124-                          (format s (compatfmt "~@<Retry ASDF operation.~@:>"))))
  8.8125-              (clear-configuration-and-retry ()
  8.8126-                :report (lambda (s)
  8.8127-                          (format s (compatfmt "~@<Retry ASDF operation after resetting the configuration.~@:>")))
  8.8128-                (unless (null *asdf-session*)
  8.8129-                  (clrhash (session-cache *asdf-session*)))
  8.8130-                (clear-configuration)))))))
  8.8131-
  8.8132-  ;; Syntactic sugar for call-with-asdf-session
  8.8133-  (defmacro with-asdf-session ((&key key override override-cache override-forcing) &body body)
  8.8134-    `(call-with-asdf-session
  8.8135-      #'(lambda () ,@body)
  8.8136-      :override ,override :key ,key
  8.8137-      :override-cache ,override-cache :override-forcing ,override-forcing))
  8.8138-
  8.8139-
  8.8140-  ;;; Define specific accessor for file (date) stamp.
  8.8141-
  8.8142-  ;; Normalize a namestring for use as a key in the session cache.
  8.8143-  (defun normalize-namestring (pathname)
  8.8144-    (let ((resolved (resolve-symlinks*
  8.8145-                     (ensure-absolute-pathname
  8.8146-                      (physicalize-pathname pathname)
  8.8147-                      'get-pathname-defaults))))
  8.8148-      (with-pathname-defaults () (namestring resolved))))
  8.8149-
  8.8150-  ;; Compute the file stamp for a normalized namestring
  8.8151-  (defun compute-file-stamp (normalized-namestring)
  8.8152-    (with-pathname-defaults ()
  8.8153-      (or (safe-file-write-date normalized-namestring) t)))
  8.8154-
  8.8155-  ;; Override the time STAMP associated to a given FILE in the session cache.
  8.8156-  ;; If no STAMP is specified, recompute a new one from the filesystem.
  8.8157-  (defun register-file-stamp (file &optional (stamp nil stampp))
  8.8158-    (let* ((namestring (normalize-namestring file))
  8.8159-           (stamp (if stampp stamp (compute-file-stamp namestring))))
  8.8160-      (set-asdf-cache-entry `(get-file-stamp ,namestring) (list stamp))))
  8.8161-
  8.8162-  ;; Get or compute a memoized stamp for given FILE from the session cache.
  8.8163-  (defun get-file-stamp (file)
  8.8164-    (when file
  8.8165-      (let ((namestring (normalize-namestring file)))
  8.8166-        (do-asdf-cache `(get-file-stamp ,namestring) (compute-file-stamp namestring)))))
  8.8167-
  8.8168-
  8.8169-  ;;; Conditions
  8.8170-
  8.8171-  (define-condition system-definition-error (error) ()
  8.8172-    ;; [this use of :report should be redundant, but unfortunately it's not.
  8.8173-    ;; cmucl's lisp::output-instance prefers the kernel:slot-class-print-function
  8.8174-    ;; over print-object; this is always conditions::%print-condition for
  8.8175-    ;; condition objects, which in turn does inheritance of :report options at
  8.8176-    ;; run-time.  fortunately, inheritance means we only need this kludge here in
  8.8177-    ;; order to fix all conditions that build on it.  -- rgr, 28-Jul-02.]
  8.8178-    #+cmucl (:report print-object))
  8.8179-
  8.8180-  (define-condition formatted-system-definition-error (system-definition-error)
  8.8181-    ((format-control :initarg :format-control :reader format-control)
  8.8182-     (format-arguments :initarg :format-arguments :reader format-arguments))
  8.8183-    (:report (lambda (c s)
  8.8184-               (apply 'format s (format-control c) (format-arguments c)))))
  8.8185-
  8.8186-  (defun sysdef-error (format &rest arguments)
  8.8187-    (error 'formatted-system-definition-error :format-control
  8.8188-           format :format-arguments arguments)))
  8.8189-;;;; -------------------------------------------------------------------------
  8.8190-;;;; Components
  8.8191-
  8.8192-(uiop/package:define-package :asdf/component
  8.8193-  (:recycle :asdf/component :asdf/find-component :asdf)
  8.8194-  (:use :uiop/common-lisp :uiop :asdf/upgrade :asdf/session)
  8.8195-  (:export
  8.8196-   #:component #:component-find-path
  8.8197-   #:find-component ;; methods defined in find-component
  8.8198-   #:component-name #:component-pathname #:component-relative-pathname
  8.8199-   #:component-parent #:component-system #:component-parent-pathname
  8.8200-   #:child-component #:parent-component #:module
  8.8201-   #:file-component
  8.8202-   #:source-file #:c-source-file #:java-source-file
  8.8203-   #:static-file #:doc-file #:html-file
  8.8204-   #:file-type
  8.8205-   #:source-file-type #:source-file-explicit-type ;; backward-compatibility
  8.8206-   #:component-in-order-to #:component-sideway-dependencies
  8.8207-   #:component-if-feature #:around-compile-hook
  8.8208-   #:component-description #:component-long-description
  8.8209-   #:component-version #:version-satisfies
  8.8210-   #:component-inline-methods ;; backward-compatibility only. DO NOT USE!
  8.8211-   #:component-operation-times ;; For internal use only.
  8.8212-   ;; portable ASDF encoding and implementation-specific external-format
  8.8213-   #:component-external-format #:component-encoding
  8.8214-   #:component-children-by-name #:component-children #:compute-children-by-name
  8.8215-   #:component-build-operation
  8.8216-   #:module-default-component-class
  8.8217-   #:module-components ;; backward-compatibility. DO NOT USE.
  8.8218-   #:sub-components
  8.8219-
  8.8220-   ;; conditions
  8.8221-   #:duplicate-names
  8.8222-
  8.8223-   ;; Internals we'd like to share with the ASDF package, especially for upgrade purposes
  8.8224-   #:name #:version #:description #:long-description #:author #:maintainer #:licence
  8.8225-   #:components-by-name #:components #:children #:children-by-name
  8.8226-   #:default-component-class #:source-file
  8.8227-   #:defsystem-depends-on ; This symbol retained for backward compatibility.
  8.8228-   #:sideway-dependencies #:if-feature #:in-order-to #:inline-methods
  8.8229-   #:relative-pathname #:absolute-pathname #:operation-times #:around-compile
  8.8230-   #:%encoding #:properties #:component-properties #:parent))
  8.8231-(in-package :asdf/component)
  8.8232-
  8.8233-(with-upgradability ()
  8.8234-  (defgeneric component-name (component)
  8.8235-    (:documentation "Name of the COMPONENT, unique relative to its parent"))
  8.8236-  (defgeneric component-system (component)
  8.8237-    (:documentation "Top-level system containing the COMPONENT"))
  8.8238-  (defgeneric component-pathname (component)
  8.8239-    (:documentation "Pathname of the COMPONENT if any, or NIL."))
  8.8240-  (defgeneric component-relative-pathname (component)
  8.8241-    ;; in ASDF4, rename that to component-specified-pathname ?
  8.8242-    (:documentation "Specified pathname of the COMPONENT,
  8.8243-intended to be merged with the pathname of that component's parent if any, using merged-pathnames*.
  8.8244-Despite the function's name, the return value can be an absolute pathname, in which case the merge
  8.8245-will leave it unmodified."))
  8.8246-  (defgeneric component-external-format (component)
  8.8247-    (:documentation "The external-format of the COMPONENT.
  8.8248-By default, deduced from the COMPONENT-ENCODING."))
  8.8249-  (defgeneric component-encoding (component)
  8.8250-    (:documentation "The encoding of the COMPONENT. By default, only :utf-8 is supported.
  8.8251-Use asdf-encodings to support more encodings."))
  8.8252-  (defgeneric version-satisfies (component version)
  8.8253-    (:documentation "Check whether a COMPONENT satisfies the constraint of being at least as recent
  8.8254-as the specified VERSION, which must be a string of dot-separated natural numbers, or NIL."))
  8.8255-  (defgeneric component-version (component)
  8.8256-    (:documentation "Return the version of a COMPONENT, which must be a string of dot-separated
  8.8257-natural numbers, or NIL."))
  8.8258-  (defgeneric (setf component-version) (new-version component)
  8.8259-    (:documentation "Updates the version of a COMPONENT, which must be a string of dot-separated
  8.8260-natural numbers, or NIL."))
  8.8261-  (defgeneric component-parent (component)
  8.8262-    (:documentation "The parent of a child COMPONENT,
  8.8263-or NIL for top-level components (a.k.a. systems)"))
  8.8264-  ;; NIL is a designator for the absence of a component, in which case the parent is also absent.
  8.8265-  (defmethod component-parent ((component null)) nil)
  8.8266-
  8.8267-  ;; Deprecated: Backward compatible way of computing the FILE-TYPE of a component.
  8.8268-  (with-asdf-deprecation (:style-warning "3.4")
  8.8269-   (defgeneric source-file-type (component system)
  8.8270-     (:documentation "DEPRECATED. Use the FILE-TYPE of a COMPONENT instead.")))
  8.8271-
  8.8272-  (define-condition duplicate-names (system-definition-error)
  8.8273-    ((name :initarg :name :reader duplicate-names-name))
  8.8274-    (:report (lambda (c s)
  8.8275-               (format s (compatfmt "~@<Error while defining system: multiple components are given same name ~S~@:>")
  8.8276-                       (duplicate-names-name c))))))
  8.8277-
  8.8278-
  8.8279-(with-upgradability ()
  8.8280-  (defclass component ()
  8.8281-    ((name :accessor component-name :initarg :name :type string :documentation
  8.8282-           "Component name: designator for a string composed of portable pathname characters")
  8.8283-     ;; We might want to constrain version with
  8.8284-     ;; :type (and string (satisfies parse-version))
  8.8285-     ;; but we cannot until we fix all systems that don't use it correctly!
  8.8286-     (version :accessor component-version :initarg :version :initform nil)
  8.8287-     (description :accessor component-description :initarg :description :initform nil)
  8.8288-     (long-description :accessor component-long-description :initarg :long-description :initform nil)
  8.8289-     (sideway-dependencies :accessor component-sideway-dependencies :initform nil)
  8.8290-     (if-feature :accessor component-if-feature :initform nil :initarg :if-feature)
  8.8291-     ;; In the ASDF object model, dependencies exist between *actions*,
  8.8292-     ;; where an action is a pair of an operation and a component.
  8.8293-     ;; Dependencies are represented as alists of operations
  8.8294-     ;; to a list where each entry is a pair of an operation and a list of component specifiers.
  8.8295-     ;; Up until ASDF 2.26.9, there used to be two kinds of dependencies:
  8.8296-     ;; in-order-to and do-first, each stored in its own slot. Now there is only in-order-to.
  8.8297-     ;; in-order-to used to represent things that modify the filesystem (such as compiling a fasl)
  8.8298-     ;; and do-first things that modify the current image (such as loading a fasl).
  8.8299-     ;; These are now unified because we now correctly propagate timestamps between dependencies.
  8.8300-     ;; Happily, no one seems to have used do-first too much (especially since until ASDF 2.017,
  8.8301-     ;; anything you specified was overridden by ASDF itself anyway), but the name in-order-to remains.
  8.8302-     ;; The names are bad, but they have been the official API since Dan Barlow's ASDF 1.52!
  8.8303-     ;; LispWorks's defsystem has caused-by and requires for in-order-to and do-first respectively.
  8.8304-     ;; Maybe rename the slots in ASDF? But that's not very backward-compatible.
  8.8305-     ;; See our ASDF 2 paper for more complete explanations.
  8.8306-     (in-order-to :initform nil :initarg :in-order-to
  8.8307-                  :accessor component-in-order-to)
  8.8308-     ;; Methods defined using the "inline" style inside a defsystem form:
  8.8309-     ;; we store them here so we can delete them when the system is re-evaluated.
  8.8310-     (inline-methods :accessor component-inline-methods :initform nil)
  8.8311-     ;; ASDF4: rename it from relative-pathname to specified-pathname. It need not be relative.
  8.8312-     ;; There is no initform and no direct accessor for this specified pathname,
  8.8313-     ;; so we only access the information through appropriate methods, after it has been processed.
  8.8314-     ;; Unhappily, some braindead systems directly access the slot. Make them stop before ASDF4.
  8.8315-     (relative-pathname :initarg :pathname)
  8.8316-     ;; The absolute-pathname is computed based on relative-pathname and parent pathname.
  8.8317-     ;; The slot is but a cache used by component-pathname.
  8.8318-     (absolute-pathname)
  8.8319-     (operation-times :initform (make-hash-table)
  8.8320-                      :accessor component-operation-times)
  8.8321-     (around-compile :initarg :around-compile)
  8.8322-     ;; Properties are for backward-compatibility with ASDF2 only. DO NOT USE!
  8.8323-     (properties :accessor component-properties :initarg :properties
  8.8324-                 :initform nil)
  8.8325-     (%encoding :accessor %component-encoding :initform nil :initarg :encoding)
  8.8326-     ;; For backward-compatibility, this slot is part of component rather than of child-component. ASDF4: stop it.
  8.8327-     (parent :initarg :parent :initform nil :reader component-parent)
  8.8328-     (build-operation
  8.8329-      :initarg :build-operation :initform nil :reader component-build-operation)
  8.8330-     ;; Cache for ADDITIONAL-INPUT-FILES function.
  8.8331-     (additional-input-files :accessor %additional-input-files :initform nil))
  8.8332-    (:documentation "Base class for all components of a build"))
  8.8333-
  8.8334-  (defgeneric find-component (base path &key registered)
  8.8335-    (:documentation "Find a component by resolving the PATH starting from BASE parent.
  8.8336-If REGISTERED is true, only search currently registered systems."))
  8.8337-
  8.8338-  (defun component-find-path (component)
  8.8339-    "Return a path from a root system to the COMPONENT.
  8.8340-The return value is a list of component NAMES; a list of strings."
  8.8341-    (check-type component (or null component))
  8.8342-    (reverse
  8.8343-     (loop :for c = component :then (component-parent c)
  8.8344-           :while c :collect (component-name c))))
  8.8345-
  8.8346-  (defmethod print-object ((c component) stream)
  8.8347-    (print-unreadable-object (c stream :type t :identity nil)
  8.8348-      (format stream "~{~S~^ ~}" (component-find-path c))))
  8.8349-
  8.8350-  (defmethod component-system ((component component))
  8.8351-    (if-let (system (component-parent component))
  8.8352-      (component-system system)
  8.8353-      component)))
  8.8354-
  8.8355-
  8.8356-;;;; Component hierarchy within a system
  8.8357-;; The tree typically but not necessarily follows the filesystem hierarchy.
  8.8358-(with-upgradability ()
  8.8359-  (defclass child-component (component) ()
  8.8360-    (:documentation "A CHILD-COMPONENT is a COMPONENT that may be part of
  8.8361-a PARENT-COMPONENT."))
  8.8362-
  8.8363-  (defclass file-component (child-component)
  8.8364-    ((type :accessor file-type :initarg :type)) ; no default
  8.8365-    (:documentation "a COMPONENT that represents a file"))
  8.8366-  (defclass source-file (file-component)
  8.8367-    ((type :accessor source-file-explicit-type ;; backward-compatibility
  8.8368-           :initform nil))) ;; NB: many systems have come to rely on this default.
  8.8369-  (defclass c-source-file (source-file)
  8.8370-    ((type :initform "c")))
  8.8371-  (defclass java-source-file (source-file)
  8.8372-    ((type :initform "java")))
  8.8373-  (defclass static-file (source-file)
  8.8374-    ((type :initform nil))
  8.8375-    (:documentation "Component for a file to be included as is in the build output"))
  8.8376-  (defclass doc-file (static-file) ())
  8.8377-  (defclass html-file (doc-file)
  8.8378-    ((type :initform "html")))
  8.8379-
  8.8380-  (defclass parent-component (component)
  8.8381-    ((children
  8.8382-      :initform nil
  8.8383-      :initarg :components
  8.8384-      :reader module-components ; backward-compatibility
  8.8385-      :accessor component-children)
  8.8386-     (children-by-name
  8.8387-      :reader module-components-by-name ; backward-compatibility
  8.8388-      :accessor component-children-by-name)
  8.8389-     (default-component-class
  8.8390-      :initform nil
  8.8391-      :initarg :default-component-class
  8.8392-      :accessor module-default-component-class))
  8.8393-  (:documentation "A PARENT-COMPONENT is a component that may have children.")))
  8.8394-
  8.8395-(with-upgradability ()
  8.8396-  ;; (Private) Function that given a PARENT component,
  8.8397-  ;; the list of children of which has been initialized,
  8.8398-  ;; compute the hash-table in slot children-by-name that allows to retrieve its children by name.
  8.8399-  ;; If ONLY-IF-NEEDED-P is defined, skip any (re)computation if the slot is already populated.
  8.8400-  (defun compute-children-by-name (parent &key only-if-needed-p)
  8.8401-    (unless (and only-if-needed-p (slot-boundp parent 'children-by-name))
  8.8402-      (let ((hash (make-hash-table :test 'equal)))
  8.8403-        (setf (component-children-by-name parent) hash)
  8.8404-        (loop :for c :in (component-children parent)
  8.8405-              :for name = (component-name c)
  8.8406-              :for previous = (gethash name hash)
  8.8407-              :do (when previous (error 'duplicate-names :name name))
  8.8408-                  (setf (gethash name hash) c))
  8.8409-        hash))))
  8.8410-
  8.8411-(with-upgradability ()
  8.8412-  (defclass module (child-component parent-component)
  8.8413-    (#+clisp (components)) ;; backward compatibility during upgrade only
  8.8414-    (:documentation "A module is a intermediate component with both a parent and children,
  8.8415-typically but not necessarily representing the files in a subdirectory of the build source.")))
  8.8416-
  8.8417-
  8.8418-;;;; component pathnames
  8.8419-(with-upgradability ()
  8.8420-  (defgeneric component-parent-pathname (component)
  8.8421-    (:documentation "The pathname of the COMPONENT's parent, if any, or NIL"))
  8.8422-  (defmethod component-parent-pathname (component)
  8.8423-    (component-pathname (component-parent component)))
  8.8424-
  8.8425-  ;; The default method for component-pathname tries to extract a cached precomputed
  8.8426-  ;; absolute-pathname from the relevant slot, and if not, computes it by merging the
  8.8427-  ;; component-relative-pathname (which should be component-specified-pathname, it can be absolute)
  8.8428-  ;; with the directory of the component-parent-pathname.
  8.8429-  (defmethod component-pathname ((component component))
  8.8430-    (if (slot-boundp component 'absolute-pathname)
  8.8431-        (slot-value component 'absolute-pathname)
  8.8432-        (let ((pathname
  8.8433-                (merge-pathnames*
  8.8434-                 (component-relative-pathname component)
  8.8435-                 (pathname-directory-pathname (component-parent-pathname component)))))
  8.8436-          (unless (or (null pathname) (absolute-pathname-p pathname))
  8.8437-            (error (compatfmt "~@<Invalid relative pathname ~S for component ~S~@:>")
  8.8438-                   pathname (component-find-path component)))
  8.8439-          (setf (slot-value component 'absolute-pathname) pathname)
  8.8440-          pathname)))
  8.8441-
  8.8442-  ;; Default method for component-relative-pathname:
  8.8443-  ;; combine the contents of slot relative-pathname (from specified initarg :pathname)
  8.8444-  ;; with the appropriate source-file-type, which defaults to the file-type of the component.
  8.8445-  (defmethod component-relative-pathname ((component component))
  8.8446-    ;; SOURCE-FILE-TYPE below is strictly for backward-compatibility with ASDF1.
  8.8447-    ;; We ought to be able to extract this from the component alone with FILE-TYPE.
  8.8448-    ;; TODO: track who uses it in Quicklisp, and have them not use it anymore;
  8.8449-    ;; maybe issue a WARNING (then eventually CERROR) if the two methods diverge?
  8.8450-    (let (#+abcl
  8.8451-          (parent
  8.8452-            (component-parent-pathname component)))
  8.8453-      (parse-unix-namestring
  8.8454-       (or (and (slot-boundp component 'relative-pathname)
  8.8455-                (slot-value component 'relative-pathname))
  8.8456-           (component-name component))
  8.8457-       :want-relative
  8.8458-       #-abcl t
  8.8459-       ;; JAR-PATHNAMES always have absolute directories
  8.8460-       #+abcl (not (ext:pathname-jar-p parent))
  8.8461-       :type (source-file-type component (component-system component))
  8.8462-       :defaults (component-parent-pathname component))))
  8.8463-
  8.8464-  (defmethod source-file-type ((component parent-component) (system parent-component))
  8.8465-    :directory)
  8.8466-
  8.8467-  (defmethod source-file-type ((component file-component) (system parent-component))
  8.8468-    (file-type component)))
  8.8469-
  8.8470-
  8.8471-;;;; Encodings
  8.8472-(with-upgradability ()
  8.8473-  (defmethod component-encoding ((c component))
  8.8474-    (or (loop :for x = c :then (component-parent x)
  8.8475-              :while x :thereis (%component-encoding x))
  8.8476-        (detect-encoding (component-pathname c))))
  8.8477-
  8.8478-  (defmethod component-external-format ((c component))
  8.8479-    (encoding-external-format (component-encoding c))))
  8.8480-
  8.8481-
  8.8482-;;;; around-compile-hook
  8.8483-(with-upgradability ()
  8.8484-  (defgeneric around-compile-hook (component)
  8.8485-    (:documentation "An optional hook function that will be called with one argument, a thunk.
  8.8486-The hook function must call the thunk, that will compile code from the component, and may or may not
  8.8487-also evaluate the compiled results. The hook function may establish dynamic variable bindings around
  8.8488-this compilation, or check its results, etc."))
  8.8489-  (defmethod around-compile-hook ((c component))
  8.8490-    (cond
  8.8491-      ((slot-boundp c 'around-compile)
  8.8492-       (slot-value c 'around-compile))
  8.8493-      ((component-parent c)
  8.8494-       (around-compile-hook (component-parent c))))))
  8.8495-
  8.8496-
  8.8497-;;;; version-satisfies
  8.8498-(with-upgradability ()
  8.8499-  ;; short-circuit testing of null version specifications.
  8.8500-  ;; this is an all-pass, without warning
  8.8501-  (defmethod version-satisfies :around ((c t) (version null))
  8.8502-    t)
  8.8503-  (defmethod version-satisfies ((c component) version)
  8.8504-    (unless (and version (slot-boundp c 'version) (component-version c))
  8.8505-      (when version
  8.8506-        (warn "Requested version ~S but ~S has no version" version c))
  8.8507-      (return-from version-satisfies nil))
  8.8508-    (version-satisfies (component-version c) version))
  8.8509-
  8.8510-  (defmethod version-satisfies ((cver string) version)
  8.8511-    (version<= version cver)))
  8.8512-
  8.8513-
  8.8514-;;; all sub-components (of a given type)
  8.8515-(with-upgradability ()
  8.8516-  (defun sub-components (component &key (type t))
  8.8517-    "Compute the transitive sub-components of given COMPONENT that are of given TYPE"
  8.8518-    (while-collecting (c)
  8.8519-      (labels ((recurse (x)
  8.8520-                 (when (if-let (it (component-if-feature x)) (featurep it) t)
  8.8521-                   (when (typep x type)
  8.8522-                     (c x))
  8.8523-                   (when (typep x 'parent-component)
  8.8524-                     (map () #'recurse (component-children x))))))
  8.8525-        (recurse component)))))
  8.8526-
  8.8527-;;;; -------------------------------------------------------------------------
  8.8528-;;;; Operations
  8.8529-
  8.8530-(uiop/package:define-package :asdf/operation
  8.8531-  (:recycle :asdf/operation :asdf/action :asdf) ;; asdf/action for FEATURE pre 2.31.5.
  8.8532-  (:use :uiop/common-lisp :uiop :asdf/upgrade :asdf/session)
  8.8533-  (:export
  8.8534-   #:operation
  8.8535-   #:*operations* #:make-operation #:find-operation
  8.8536-   #:feature)) ;; TODO: stop exporting the deprecated FEATURE feature.
  8.8537-(in-package :asdf/operation)
  8.8538-
  8.8539-;;; Operation Classes
  8.8540-(when-upgrading (:version "2.27" :when (find-class 'operation nil))
  8.8541-  ;; override any obsolete shared-initialize method when upgrading from ASDF2.
  8.8542-  (defmethod shared-initialize :after ((o operation) (slot-names t) &key)
  8.8543-    (values)))
  8.8544-
  8.8545-(with-upgradability ()
  8.8546-  (defclass operation ()
  8.8547-    ()
  8.8548-    (:documentation "The base class for all ASDF operations.
  8.8549-
  8.8550-ASDF does NOT and never did distinguish between multiple operations of the same class.
  8.8551-Therefore, all slots of all operations MUST have :allocation :class and no initargs. No exceptions.
  8.8552-"))
  8.8553-
  8.8554-  (defvar *in-make-operation* nil)
  8.8555-
  8.8556-  (defun check-operation-constructor ()
  8.8557-    "Enforce that OPERATION instances must be created with MAKE-OPERATION."
  8.8558-    (unless *in-make-operation*
  8.8559-      (sysdef-error "OPERATION instances must only be created through MAKE-OPERATION.")))
  8.8560-
  8.8561-  (defmethod print-object ((o operation) stream)
  8.8562-    (print-unreadable-object (o stream :type t :identity nil)))
  8.8563-
  8.8564-  ;;; Override previous methods (from 3.1.7 and earlier) and add proper error checking.
  8.8565-  #-genera ;; Genera adds its own system initargs, e.g. clos-internals:storage-area 8
  8.8566-  (defmethod initialize-instance :after ((o operation) &rest initargs &key &allow-other-keys)
  8.8567-    (unless (null initargs)
  8.8568-      (parameter-error "~S does not accept initargs" 'operation))))
  8.8569-
  8.8570-
  8.8571-;;; make-operation, find-operation
  8.8572-
  8.8573-(with-upgradability ()
  8.8574-  ;; A table to memoize instances of a given operation. There shall be only one.
  8.8575-  (defparameter* *operations* (make-hash-table :test 'equal))
  8.8576-
  8.8577-  ;; A memoizing way of creating instances of operation.
  8.8578-  (defun make-operation (operation-class)
  8.8579-    "This function creates and memoizes an instance of OPERATION-CLASS.
  8.8580-All operation instances MUST be created through this function.
  8.8581-
  8.8582-Use of INITARGS is not supported at this time."
  8.8583-    (let ((class (coerce-class operation-class
  8.8584-                               :package :asdf/interface :super 'operation :error 'sysdef-error))
  8.8585-          (*in-make-operation* t))
  8.8586-      (ensure-gethash class *operations* `(make-instance ,class))))
  8.8587-
  8.8588-  ;; This function is mostly for backward and forward compatibility:
  8.8589-  ;; operations used to preserve the operation-original-initargs of the context,
  8.8590-  ;; and may in the future preserve some operation-canonical-initargs.
  8.8591-  ;; Still, the treatment of NIL as a disabling context is useful in some cases.
  8.8592-  (defgeneric find-operation (context spec)
  8.8593-    (:documentation "Find an operation by resolving the SPEC in the CONTEXT"))
  8.8594-  (defmethod find-operation ((context t) (spec operation))
  8.8595-    spec)
  8.8596-  (defmethod find-operation ((context t) (spec symbol))
  8.8597-    (when spec ;; NIL designates itself, i.e. absence of operation
  8.8598-      (make-operation spec))) ;; TODO: preserve the (operation-canonical-initargs context)
  8.8599-  (defmethod find-operation ((context t) (spec string))
  8.8600-    (make-operation spec))) ;; TODO: preserve the (operation-canonical-initargs context)
  8.8601-
  8.8602-;;;; -------------------------------------------------------------------------
  8.8603-;;;; Systems
  8.8604-
  8.8605-(uiop/package:define-package :asdf/system
  8.8606-  (:recycle :asdf :asdf/system :asdf/find-system)
  8.8607-  (:use :uiop/common-lisp :uiop :asdf/upgrade :asdf/session :asdf/component)
  8.8608-  (:export
  8.8609-   #:system #:proto-system #:undefined-system #:reset-system-class
  8.8610-   #:system-source-file #:system-source-directory #:system-relative-pathname
  8.8611-   #:system-description #:system-long-description
  8.8612-   #:system-author #:system-maintainer #:system-licence #:system-license
  8.8613-   #:system-version
  8.8614-   #:definition-dependency-list #:definition-dependency-set #:system-defsystem-depends-on
  8.8615-   #:system-depends-on #:system-weakly-depends-on
  8.8616-   #:component-build-pathname #:build-pathname
  8.8617-   #:component-entry-point #:entry-point
  8.8618-   #:homepage #:system-homepage
  8.8619-   #:bug-tracker #:system-bug-tracker
  8.8620-   #:mailto #:system-mailto
  8.8621-   #:long-name #:system-long-name
  8.8622-   #:source-control #:system-source-control
  8.8623-   #:coerce-name #:primary-system-name #:primary-system-p #:coerce-filename
  8.8624-   #:find-system #:builtin-system-p)) ;; forward-reference, defined in find-system
  8.8625-(in-package :asdf/system)
  8.8626-
  8.8627-(with-upgradability ()
  8.8628-  ;; The method is actually defined in asdf/find-system,
  8.8629-  ;; but we declare the function here to avoid a forward reference.
  8.8630-  (defgeneric find-system (system &optional error-p)
  8.8631-    (:documentation "Given a system designator, find the actual corresponding system object.
  8.8632-If no system is found, then signal an error if ERROR-P is true (the default), or else return NIL.
  8.8633-A system designator is usually a string (conventionally all lowercase) or a symbol, designating
  8.8634-the same system as its downcased name; it can also be a system object (designating itself)."))
  8.8635-
  8.8636-  (defgeneric system-source-file (system)
  8.8637-    (:documentation "Return the source file in which system is defined."))
  8.8638-
  8.8639-  ;; This is bad design, but was the easiest kluge I found to let the user specify that
  8.8640-  ;; some special actions create outputs at locations controled by the user that are not affected
  8.8641-  ;; by the usual output-translations.
  8.8642-  ;; TODO: Fix operate to stop passing flags to operation (which in the current design shouldn't
  8.8643-  ;; have any flags, since the stamp cache, etc., can't distinguish them), and instead insert
  8.8644-  ;; *there* the ability of specifying special output paths, not in the system definition.
  8.8645-  (defgeneric component-build-pathname (component)
  8.8646-    (:documentation "The COMPONENT-BUILD-PATHNAME, when defined and not null, specifies the
  8.8647-output pathname for the action using the COMPONENT-BUILD-OPERATION.
  8.8648-
  8.8649-NB: This interface is subject to change. Please contact ASDF maintainers if you use it."))
  8.8650-
  8.8651-  ;; TODO: Should this have been made a SYSTEM-ENTRY-POINT instead?
  8.8652-  (defgeneric component-entry-point (component)
  8.8653-    (:documentation "The COMPONENT-ENTRY-POINT, when defined, specifies what function to call
  8.8654-(with no argument) when running an image dumped from the COMPONENT.
  8.8655-
  8.8656-NB: This interface is subject to change. Please contact ASDF maintainers if you use it."))
  8.8657-
  8.8658-  (defmethod component-entry-point ((c component))
  8.8659-    nil))
  8.8660-
  8.8661-
  8.8662-;;;; The system class
  8.8663-
  8.8664-(with-upgradability ()
  8.8665-  (defclass proto-system () ; slots to keep when resetting a system
  8.8666-    ;; To preserve identity for all objects, we'd need keep the components slots
  8.8667-    ;; but also to modify parse-component-form to reset the recycled objects.
  8.8668-    ((name)
  8.8669-     (source-file)
  8.8670-     ;; These two slots contains the *inferred* dependencies of define-op,
  8.8671-     ;; from loading the .asd file, as list and as set.
  8.8672-     (definition-dependency-list
  8.8673-         :initform nil :accessor definition-dependency-list)
  8.8674-     (definition-dependency-set
  8.8675-         :initform (list-to-hash-set nil) :accessor definition-dependency-set))
  8.8676-    (:documentation "PROTO-SYSTEM defines the elements of identity that are preserved when
  8.8677-a SYSTEM is redefined and its class is modified."))
  8.8678-
  8.8679-  (defclass system (module proto-system)
  8.8680-    ;; Backward-compatibility: inherit from module. ASDF4: only inherit from parent-component.
  8.8681-    (;; {,long-}description is now inherited from component, but we add the legacy accessors
  8.8682-     (description :writer (setf system-description))
  8.8683-     (long-description :writer (setf system-long-description))
  8.8684-     (author :writer (setf system-author) :initarg :author :initform nil)
  8.8685-     (maintainer :writer (setf system-maintainer) :initarg :maintainer :initform nil)
  8.8686-     (licence :writer (setf system-licence) :initarg :licence
  8.8687-              :writer (setf system-license) :initarg :license
  8.8688-              :initform nil)
  8.8689-     (homepage :writer (setf system-homepage) :initarg :homepage :initform nil)
  8.8690-     (bug-tracker :writer (setf system-bug-tracker) :initarg :bug-tracker :initform nil)
  8.8691-     (mailto :writer (setf system-mailto) :initarg :mailto :initform nil)
  8.8692-     (long-name :writer (setf system-long-name) :initarg :long-name :initform nil)
  8.8693-     ;; Conventions for this slot aren't clear yet as of ASDF 2.27, but whenever they are, they will be enforced.
  8.8694-     ;; I'm introducing the slot before the conventions are set for maximum compatibility.
  8.8695-     (source-control :writer (setf system-source-control) :initarg :source-control :initform nil)
  8.8696-
  8.8697-     (builtin-system-p :accessor builtin-system-p :initform nil :initarg :builtin-system-p)
  8.8698-     (build-pathname
  8.8699-      :initform nil :initarg :build-pathname :accessor component-build-pathname)
  8.8700-     (entry-point
  8.8701-      :initform nil :initarg :entry-point :accessor component-entry-point)
  8.8702-     (source-file :initform nil :initarg :source-file :accessor system-source-file)
  8.8703-     ;; This slot contains the *declared* defsystem-depends-on dependencies
  8.8704-     (defsystem-depends-on :reader system-defsystem-depends-on :initarg :defsystem-depends-on
  8.8705-                           :initform nil)
  8.8706-     ;; these two are specially set in parse-component-form, so have no :INITARGs.
  8.8707-     (depends-on :reader system-depends-on :initform nil)
  8.8708-     (weakly-depends-on :reader system-weakly-depends-on :initform nil))
  8.8709-    (:documentation "SYSTEM is the base class for top-level components that users may request
  8.8710-ASDF to build."))
  8.8711-
  8.8712-  (defclass undefined-system (system) ()
  8.8713-    (:documentation "System that was not defined yet."))
  8.8714-
  8.8715-  (defun reset-system-class (system new-class &rest keys &key &allow-other-keys)
  8.8716-    "Erase any data from a SYSTEM except its basic identity, then reinitialize it
  8.8717-based on supplied KEYS."
  8.8718-    (change-class (change-class system 'proto-system) new-class)
  8.8719-    (apply 'reinitialize-instance system keys)))
  8.8720-
  8.8721-
  8.8722-;;; Canonicalizing system names
  8.8723-
  8.8724-(with-upgradability ()
  8.8725-  (defun coerce-name (name)
  8.8726-    "Given a designator for a component NAME, return the name as a string.
  8.8727-The designator can be a COMPONENT (designing its name; note that a SYSTEM is a component),
  8.8728-a SYMBOL (designing its name, downcased), or a STRING (designing itself)."
  8.8729-    (typecase name
  8.8730-      (component (component-name name))
  8.8731-      (symbol (string-downcase name))
  8.8732-      (string name)
  8.8733-      (t (sysdef-error (compatfmt "~@<Invalid component designator: ~3i~_~A~@:>") name))))
  8.8734-
  8.8735-  (defun primary-system-name (system-designator)
  8.8736-    "Given a system designator NAME, return the name of the corresponding
  8.8737-primary system, after which the .asd file in which it is defined is named.
  8.8738-If given a string or symbol (to downcase), do it syntactically
  8.8739- by stripping anything from the first slash on.
  8.8740-If given a component, do it semantically by extracting
  8.8741-the system-primary-system-name of its system from its source-file if any,
  8.8742-falling back to the syntactic criterion if none."
  8.8743-    (etypecase system-designator
  8.8744-      (string (if-let (p (position #\/ system-designator))
  8.8745-                (subseq system-designator 0 p) system-designator))
  8.8746-      (symbol (primary-system-name (coerce-name system-designator)))
  8.8747-      (component (let* ((system (component-system system-designator))
  8.8748-                        (source-file (physicalize-pathname (system-source-file system))))
  8.8749-                   (if source-file
  8.8750-                       (and (equal (pathname-type source-file) "asd")
  8.8751-                            (pathname-name source-file))
  8.8752-                       (primary-system-name (component-name system)))))))
  8.8753-
  8.8754-  (defun primary-system-p (system)
  8.8755-    "Given a system designator SYSTEM, return T if it designates a primary system, or else NIL.
  8.8756-If given a string, do it syntactically and return true if the name does not contain a slash.
  8.8757-If given a symbol, downcase to a string then fallback to previous case (NB: for NIL return T).
  8.8758-If given a component, do it semantically and return T if it's a SYSTEM and its primary-system-name
  8.8759-is the same as its component-name."
  8.8760-    (etypecase system
  8.8761-      (string (not (find #\/ system)))
  8.8762-      (symbol (primary-system-p (coerce-name system)))
  8.8763-      (component (and (typep system 'system)
  8.8764-                      (equal (component-name system) (primary-system-name system))))))
  8.8765-
  8.8766-  (defun coerce-filename (name)
  8.8767-    "Coerce a system designator NAME into a string suitable as a filename component.
  8.8768-The (current) transformation is to replace characters /:\\ each by --,
  8.8769-the former being forbidden in a filename component.
  8.8770-NB: The onus is unhappily on the user to avoid clashes."
  8.8771-    (frob-substrings (coerce-name name) '("/" ":" "\\") "--")))
  8.8772-
  8.8773-
  8.8774-;;; System virtual slot readers, recursing to the primary system if needed.
  8.8775-(with-upgradability ()
  8.8776-  (defvar *system-virtual-slots* '(long-name description long-description
  8.8777-                                   author maintainer mailto
  8.8778-                                   homepage source-control
  8.8779-                                   licence version bug-tracker)
  8.8780-    "The list of system virtual slot names.")
  8.8781-  (defun system-virtual-slot-value (system slot-name)
  8.8782-    "Return SYSTEM's virtual SLOT-NAME value.
  8.8783-If SYSTEM's SLOT-NAME value is NIL and SYSTEM is a secondary system, look in
  8.8784-the primary one."
  8.8785-    (or (slot-value system slot-name)
  8.8786-        (unless (primary-system-p system)
  8.8787-          (slot-value (find-system (primary-system-name system))
  8.8788-                      slot-name))))
  8.8789-  (defmacro define-system-virtual-slot-reader (slot-name)
  8.8790-    (let ((name (intern (strcat (string :system-) (string slot-name)))))
  8.8791-      `(progn
  8.8792-         (fmakunbound ',name) ;; These were gf from defgeneric before 3.3.2.11
  8.8793-         (declaim (notinline ,name))
  8.8794-         (defun ,name (system) (system-virtual-slot-value system ',slot-name)))))
  8.8795-  (defmacro define-system-virtual-slot-readers ()
  8.8796-    `(progn ,@(mapcar (lambda (slot-name)
  8.8797-                        `(define-system-virtual-slot-reader ,slot-name))
  8.8798-                *system-virtual-slots*)))
  8.8799-  (define-system-virtual-slot-readers)
  8.8800-  (defun system-license (system)
  8.8801-    (system-virtual-slot-value system 'licence)))
  8.8802-
  8.8803-
  8.8804-;;;; Pathnames
  8.8805-
  8.8806-(with-upgradability ()
  8.8807-  ;; Resolve a system designator to a system before extracting its system-source-file
  8.8808-  (defmethod system-source-file ((system-name string))
  8.8809-    (system-source-file (find-system system-name)))
  8.8810-  (defmethod system-source-file ((system-name symbol))
  8.8811-    (when system-name
  8.8812-      (system-source-file (find-system system-name))))
  8.8813-
  8.8814-  (defun system-source-directory (system-designator)
  8.8815-    "Return a pathname object corresponding to the directory
  8.8816-in which the system specification (.asd file) is located."
  8.8817-    (pathname-directory-pathname (system-source-file system-designator)))
  8.8818-
  8.8819-  (defun system-relative-pathname (system name &key type)
  8.8820-    "Given a SYSTEM, and a (Unix-style relative path) NAME of a file (or directory) of given TYPE,
  8.8821-return the absolute pathname of a corresponding file under that system's source code pathname."
  8.8822-    (subpathname (system-source-directory system) name :type type))
  8.8823-
  8.8824-  (defmethod component-pathname ((system system))
  8.8825-    "Given a SYSTEM, and a (Unix-style relative path) NAME of a file (or directory) of given TYPE,
  8.8826-return the absolute pathname of a corresponding file under that system's source code pathname."
  8.8827-    (let ((pathname (or (call-next-method) (system-source-directory system))))
  8.8828-      (unless (and (slot-boundp system 'relative-pathname) ;; backward-compatibility with ASDF1-age
  8.8829-                   (slot-value system 'relative-pathname)) ;; systems that directly access this slot.
  8.8830-        (setf (slot-value system 'relative-pathname) pathname))
  8.8831-      pathname))
  8.8832-
  8.8833-  ;; The default method of component-relative-pathname for a system:
  8.8834-  ;; if a pathname was specified in the .asd file, it must be relative to the .asd file
  8.8835-  ;; (actually, to its truename* if *resolve-symlinks* it true, the default).
  8.8836-  ;; The method will return an *absolute* pathname, once again showing that the historical name
  8.8837-  ;; component-relative-pathname is misleading and should have been component-specified-pathname.
  8.8838-  (defmethod component-relative-pathname ((system system))
  8.8839-    (parse-unix-namestring
  8.8840-     (and (slot-boundp system 'relative-pathname)
  8.8841-          (slot-value system 'relative-pathname))
  8.8842-     :want-relative t
  8.8843-     :type :directory
  8.8844-     :ensure-absolute t
  8.8845-     :defaults (system-source-directory system)))
  8.8846-
  8.8847-  ;; A system has no parent; if some method wants to make a path "relative to its parent",
  8.8848-  ;; it will instead be relative to the system itself.
  8.8849-  (defmethod component-parent-pathname ((system system))
  8.8850-    (system-source-directory system))
  8.8851-
  8.8852-  ;; Most components don't have a specified component-build-pathname, and therefore
  8.8853-  ;; no magic redirection of their output that disregards the output-translations.
  8.8854-  (defmethod component-build-pathname ((c component))
  8.8855-    nil))
  8.8856-
  8.8857-;;;; -------------------------------------------------------------------------
  8.8858-;;;; Finding systems
  8.8859-
  8.8860-(uiop/package:define-package :asdf/system-registry
  8.8861-  (:recycle :asdf/system-registry :asdf/find-system :asdf)
  8.8862-  (:use :uiop/common-lisp :uiop :asdf/upgrade
  8.8863-    :asdf/session :asdf/component :asdf/system)
  8.8864-  (:export
  8.8865-   #:remove-entry-from-registry #:coerce-entry-to-directory
  8.8866-   #:registered-system #:register-system
  8.8867-   #:registered-systems* #:registered-systems
  8.8868-   #:clear-system #:map-systems
  8.8869-   #:*system-definition-search-functions* #:search-for-system-definition
  8.8870-   #:*central-registry* #:probe-asd #:sysdef-central-registry-search
  8.8871-   #:contrib-sysdef-search #:sysdef-find-asdf ;; backward compatibility symbols, functions removed
  8.8872-   #:sysdef-preloaded-system-search #:register-preloaded-system #:*preloaded-systems*
  8.8873-   #:find-system-if-being-defined #:mark-component-preloaded ;; forward references to asdf/find-system
  8.8874-   #:sysdef-immutable-system-search #:register-immutable-system #:*immutable-systems*
  8.8875-   #:*registered-systems* #:clear-registered-systems
  8.8876-   ;; defined in source-registry, but specially mentioned here:
  8.8877-   #:sysdef-source-registry-search))
  8.8878-(in-package :asdf/system-registry)
  8.8879-
  8.8880-(with-upgradability ()
  8.8881-  ;;; Registry of Defined Systems
  8.8882-
  8.8883-  (defvar *registered-systems* (make-hash-table :test 'equal)
  8.8884-    "This is a hash table whose keys are strings -- the names of systems --
  8.8885-and whose values are systems.
  8.8886-A system is referred to as \"registered\" if it is present in this table.")
  8.8887-
  8.8888-  (defun registered-system (name)
  8.8889-    "Return a system of given NAME that was registered already,
  8.8890-if such a system exists.  NAME is a system designator, to be
  8.8891-normalized by COERCE-NAME. The value returned is a system object,
  8.8892-or NIL if not found."
  8.8893-    (gethash (coerce-name name) *registered-systems*))
  8.8894-
  8.8895-  (defun registered-systems* ()
  8.8896-    "Return a list containing every registered system (as a system object)."
  8.8897-    (loop :for registered :being :the :hash-values :of *registered-systems*
  8.8898-          :collect registered))
  8.8899-
  8.8900-  (defun registered-systems ()
  8.8901-    "Return a list of the names of every registered system."
  8.8902-    (mapcar 'coerce-name (registered-systems*)))
  8.8903-
  8.8904-  (defun register-system (system)
  8.8905-    "Given a SYSTEM object, register it."
  8.8906-    (check-type system system)
  8.8907-    (let ((name (component-name system)))
  8.8908-      (check-type name string)
  8.8909-      (asdf-message (compatfmt "~&~@<; ~@;Registering system ~3i~_~A~@:>~%") name)
  8.8910-      (setf (gethash name *registered-systems*) system)))
  8.8911-
  8.8912-  (defun map-systems (fn)
  8.8913-    "Apply FN to each defined system.
  8.8914-
  8.8915-FN should be a function of one argument. It will be
  8.8916-called with an object of type asdf:system."
  8.8917-    (loop :for registered :being :the :hash-values :of *registered-systems*
  8.8918-          :do (funcall fn registered)))
  8.8919-
  8.8920-
  8.8921-  ;;; Preloaded systems: in the image even if you can't find source files backing them.
  8.8922-
  8.8923-  (defvar *preloaded-systems* (make-hash-table :test 'equal)
  8.8924-    "Registration table for preloaded systems.")
  8.8925-
  8.8926-  (declaim (ftype (function (t) t) mark-component-preloaded)) ; defined in asdf/find-system
  8.8927-
  8.8928-  (defun make-preloaded-system (name keys)
  8.8929-    "Make a preloaded system of given NAME with build information from KEYS"
  8.8930-    (let ((system (apply 'make-instance (getf keys :class 'system)
  8.8931-                         :name name :source-file (getf keys :source-file)
  8.8932-                         (remove-plist-keys '(:class :name :source-file) keys))))
  8.8933-      (mark-component-preloaded system)
  8.8934-      system))
  8.8935-
  8.8936-  (defun sysdef-preloaded-system-search (requested)
  8.8937-    "If REQUESTED names a system registered as preloaded, return a new system
  8.8938-with its registration information."
  8.8939-    (let ((name (coerce-name requested)))
  8.8940-      (multiple-value-bind (keys foundp) (gethash name *preloaded-systems*)
  8.8941-        (when foundp
  8.8942-          (make-preloaded-system name keys)))))
  8.8943-
  8.8944-  (defun ensure-preloaded-system-registered (name)
  8.8945-    "If there isn't a registered _defined_ system of given NAME,
  8.8946-and a there is a registered _preloaded_ system of given NAME,
  8.8947-then define and register said preloaded system."
  8.8948-    (if-let (system (and (not (registered-system name)) (sysdef-preloaded-system-search name)))
  8.8949-      (register-system system)))
  8.8950-
  8.8951-  (defun register-preloaded-system (system-name &rest keys &key (version t) &allow-other-keys)
  8.8952-    "Register a system as being preloaded. If the system has not been loaded from the filesystem
  8.8953-yet, or if its build information is later cleared with CLEAR-SYSTEM, a dummy system will be
  8.8954-registered without backing filesystem information, based on KEYS (e.g. to provide a VERSION).
  8.8955-If VERSION is the default T, and a system was already loaded, then its version will be preserved."
  8.8956-    (let ((name (coerce-name system-name)))
  8.8957-      (when (eql version t)
  8.8958-        (if-let (system (registered-system name))
  8.8959-          (setf (getf keys :version) (component-version system))))
  8.8960-      (setf (gethash name *preloaded-systems*) keys)
  8.8961-      (ensure-preloaded-system-registered system-name)))
  8.8962-
  8.8963-
  8.8964-  ;;; Immutable systems: in the image and can't be reloaded from source.
  8.8965-
  8.8966-  (defvar *immutable-systems* nil
  8.8967-    "A hash-set (equal hash-table mapping keys to T) of systems that are immutable,
  8.8968-i.e. already loaded in memory and not to be refreshed from the filesystem.
  8.8969-They will be treated specially by find-system, and passed as :force-not argument to make-plan.
  8.8970-
  8.8971-For instance, to can deliver an image with many systems precompiled, that *will not* check the
  8.8972-filesystem for them every time a user loads an extension, what more risk a problematic upgrade
  8.8973- or catastrophic downgrade, before you dump an image, you may use:
  8.8974-   (map () 'asdf:register-immutable-system (asdf:already-loaded-systems))
  8.8975-
  8.8976-Note that direct access to this variable from outside ASDF is not supported.
  8.8977-Please call REGISTER-IMMUTABLE-SYSTEM to add new immutable systems, and
  8.8978-contact maintainers if you need a stable API to do more than that.")
  8.8979-
  8.8980-  (defun sysdef-immutable-system-search (requested)
  8.8981-    (let ((name (coerce-name requested)))
  8.8982-      (when (and *immutable-systems* (gethash name *immutable-systems*))
  8.8983-        (or (registered-system requested)
  8.8984-            (error 'formatted-system-definition-error
  8.8985-                   :format-control "Requested system ~A registered as an immutable-system, ~
  8.8986-but not even registered as defined"
  8.8987-                   :format-arguments (list name))))))
  8.8988-
  8.8989-  (defun register-immutable-system (system-name &rest keys)
  8.8990-    "Register SYSTEM-NAME as preloaded and immutable.
  8.8991-It will automatically be considered as passed to FORCE-NOT in a plan."
  8.8992-    (let ((system-name (coerce-name system-name)))
  8.8993-      (apply 'register-preloaded-system system-name keys)
  8.8994-      (unless *immutable-systems*
  8.8995-        (setf *immutable-systems* (list-to-hash-set nil)))
  8.8996-      (setf (gethash system-name *immutable-systems*) t)))
  8.8997-
  8.8998-
  8.8999-  ;;; Making systems undefined.
  8.9000-
  8.9001-  (defun clear-system (system)
  8.9002-    "Clear the entry for a SYSTEM in the database of systems previously defined.
  8.9003-However if the system was registered as PRELOADED (which it is if it is IMMUTABLE),
  8.9004-then a new system with the same name will be defined and registered in its place
  8.9005-from which build details will have been cleared.
  8.9006-Note that this does NOT in any way cause any of the code of the system to be unloaded.
  8.9007-Returns T if system was or is now undefined, NIL if a new preloaded system was redefined."
  8.9008-    ;; There is no "unload" operation in Common Lisp, and
  8.9009-    ;; a general such operation cannot be portably written,
  8.9010-    ;; considering how much CL relies on side-effects to global data structures.
  8.9011-    (let ((name (coerce-name system)))
  8.9012-      (remhash name *registered-systems*)
  8.9013-      (unset-asdf-cache-entry `(find-system ,name))
  8.9014-      (not (ensure-preloaded-system-registered name))))
  8.9015-
  8.9016-  (defun clear-registered-systems ()
  8.9017-    "Clear all currently registered defined systems.
  8.9018-Preloaded systems (including immutable ones) will be reset, other systems will be de-registered."
  8.9019-    (map () 'clear-system (registered-systems)))
  8.9020-
  8.9021-
  8.9022-  ;;; Searching for system definitions
  8.9023-
  8.9024-  ;; For the sake of keeping things reasonably neat, we adopt a convention that
  8.9025-  ;; only symbols are to be pushed to this list (rather than e.g. function objects),
  8.9026-  ;; which makes upgrade easier. Also, the name of these symbols shall start with SYSDEF-
  8.9027-  (defvar *system-definition-search-functions* '()
  8.9028-    "A list that controls the ways that ASDF looks for system definitions.
  8.9029-It contains symbols to be funcalled in order, with a requested system name as argument,
  8.9030-until one returns a non-NIL result (if any), which must then be a fully initialized system object
  8.9031-with that name.")
  8.9032-
  8.9033-  ;; Initialize and/or upgrade the *system-definition-search-functions*
  8.9034-  ;; so it doesn't contain obsolete symbols, and does contain the current ones.
  8.9035-  (defun cleanup-system-definition-search-functions ()
  8.9036-    (setf *system-definition-search-functions*
  8.9037-          (append
  8.9038-           ;; Remove known-incompatible sysdef functions from old versions of asdf.
  8.9039-           ;; Order matters, so we can't just use set-difference.
  8.9040-           (let ((obsolete
  8.9041-                  '(contrib-sysdef-search sysdef-find-asdf sysdef-preloaded-system-search)))
  8.9042-             (remove-if #'(lambda (x) (member x obsolete)) *system-definition-search-functions*))
  8.9043-           ;; Tuck our defaults at the end of the list if they were absent.
  8.9044-           ;; This is imperfect, in case they were removed on purpose,
  8.9045-           ;; but then it will be the responsibility of whoever removes these symmbols
  8.9046-           ;; to upgrade asdf before he does such a thing rather than after.
  8.9047-           (remove-if #'(lambda (x) (member x *system-definition-search-functions*))
  8.9048-                      '(sysdef-central-registry-search
  8.9049-                        sysdef-source-registry-search)))))
  8.9050-  (cleanup-system-definition-search-functions)
  8.9051-
  8.9052-  ;; This (private) function does the search for a system definition using *s-d-s-f*;
  8.9053-  ;; it is to be called by locate-system.
  8.9054-  (defun search-for-system-definition (system)
  8.9055-    ;; Search for valid definitions of the system available in the current session.
  8.9056-    ;; Previous definitions as registered in *registered-systems* MUST NOT be considered;
  8.9057-    ;; they will be reconciled by locate-system then find-system.
  8.9058-    ;; There are two special treatments: first, specially search for objects being defined
  8.9059-    ;; in the current session, to avoid definition races between several files;
  8.9060-    ;; second, specially search for immutable systems, so they cannot be redefined.
  8.9061-    ;; Finally, use the search functions specified in *system-definition-search-functions*.
  8.9062-    (let ((name (coerce-name system)))
  8.9063-      (flet ((try (f) (if-let ((x (funcall f name))) (return-from search-for-system-definition x))))
  8.9064-        (try 'find-system-if-being-defined)
  8.9065-        (try 'sysdef-immutable-system-search)
  8.9066-        (map () #'try *system-definition-search-functions*))))
  8.9067-
  8.9068-
  8.9069-  ;;; The legacy way of finding a system: the *central-registry*
  8.9070-
  8.9071-  ;; This variable contains a list of directories to be lazily searched for the requested asd
  8.9072-  ;; by sysdef-central-registry-search.
  8.9073-  (defvar *central-registry* nil
  8.9074-    "A list of 'system directory designators' ASDF uses to find systems.
  8.9075-
  8.9076-A 'system directory designator' is a pathname or an expression
  8.9077-which evaluates to a pathname. For example:
  8.9078-
  8.9079-    (setf asdf:*central-registry*
  8.9080-          (list '*default-pathname-defaults*
  8.9081-                #p\"/home/me/cl/systems/\"
  8.9082-                #p\"/usr/share/common-lisp/systems/\"))
  8.9083-
  8.9084-This variable is for backward compatibility.
  8.9085-Going forward, we recommend new users should be using the source-registry.")
  8.9086-
  8.9087-  ;; Function to look for an asd file of given NAME under a directory provided by DEFAULTS.
  8.9088-  ;; Return the truename of that file if it is found and TRUENAME is true.
  8.9089-  ;; Return NIL if the file is not found.
  8.9090-  ;; On Windows, follow shortcuts to .asd files.
  8.9091-  (defun probe-asd (name defaults &key truename)
  8.9092-    (block nil
  8.9093-      (when (directory-pathname-p defaults)
  8.9094-        (if-let (file (probe-file*
  8.9095-                       (ensure-absolute-pathname
  8.9096-                        (parse-unix-namestring name :type "asd")
  8.9097-                        #'(lambda () (ensure-absolute-pathname defaults 'get-pathname-defaults nil))
  8.9098-                        nil)
  8.9099-                       :truename truename))
  8.9100-          (return file))
  8.9101-        #-(or clisp genera) ; clisp doesn't need it, plain genera doesn't have read-sequence(!)
  8.9102-        (os-cond
  8.9103-         ((os-windows-p)
  8.9104-          (when (physical-pathname-p defaults)
  8.9105-            (let ((shortcut
  8.9106-                    (make-pathname
  8.9107-                     :defaults defaults :case :local
  8.9108-                     :name (strcat name ".asd")
  8.9109-                     :type "lnk")))
  8.9110-              (when (probe-file* shortcut)
  8.9111-                (ensure-pathname (parse-windows-shortcut shortcut) :namestring :native)))))))))
  8.9112-
  8.9113-  ;; Function to push onto *s-d-s-f* to use the *central-registry*
  8.9114-  (defun sysdef-central-registry-search (system)
  8.9115-    (let ((name (primary-system-name system))
  8.9116-          (to-remove nil)
  8.9117-          (to-replace nil))
  8.9118-      (block nil
  8.9119-        (unwind-protect
  8.9120-             (dolist (dir *central-registry*)
  8.9121-               (let ((defaults (eval dir))
  8.9122-                     directorized)
  8.9123-                 (when defaults
  8.9124-                   (cond ((directory-pathname-p defaults)
  8.9125-                          (let* ((file (probe-asd name defaults :truename *resolve-symlinks*)))
  8.9126-                            (when file
  8.9127-                              (return file))))
  8.9128-                         (t
  8.9129-                          (restart-case
  8.9130-                              (let* ((*print-circle* nil)
  8.9131-                                     (message
  8.9132-                                       (format nil
  8.9133-                                               (compatfmt "~@<While searching for system ~S: ~3i~_~S evaluated to ~S which is not an absolute directory.~@:>")
  8.9134-                                               system dir defaults)))
  8.9135-                                (error message))
  8.9136-                            (remove-entry-from-registry ()
  8.9137-                              :report "Remove entry from *central-registry* and continue"
  8.9138-                              (push dir to-remove))
  8.9139-                            (coerce-entry-to-directory ()
  8.9140-                              :test (lambda (c) (declare (ignore c))
  8.9141-                                      (and (not (directory-pathname-p defaults))
  8.9142-                                           (directory-pathname-p
  8.9143-                                            (setf directorized
  8.9144-                                                  (ensure-directory-pathname defaults)))))
  8.9145-                              :report (lambda (s)
  8.9146-                                        (format s (compatfmt "~@<Coerce entry to ~a, replace ~a and continue.~@:>")
  8.9147-                                                directorized dir))
  8.9148-                              (push (cons dir directorized) to-replace))))))))
  8.9149-          ;; cleanup
  8.9150-          (dolist (dir to-remove)
  8.9151-            (setf *central-registry* (remove dir *central-registry*)))
  8.9152-          (dolist (pair to-replace)
  8.9153-            (let* ((current (car pair))
  8.9154-                   (new (cdr pair))
  8.9155-                   (position (position current *central-registry*)))
  8.9156-              (setf *central-registry*
  8.9157-                    (append (subseq *central-registry* 0 position)
  8.9158-                            (list new)
  8.9159-                            (subseq *central-registry* (1+ position)))))))))))
  8.9160-
  8.9161-;;;; -------------------------------------------------------------------------
  8.9162-;;;; Actions
  8.9163-
  8.9164-(uiop/package:define-package :asdf/action
  8.9165-  (:nicknames :asdf-action)
  8.9166-  (:recycle :asdf/action :asdf/plan :asdf)
  8.9167-  (:use :uiop/common-lisp :uiop :asdf/upgrade :asdf/session :asdf/component :asdf/operation)
  8.9168-  (:import-from :asdf/operation #:check-operation-constructor)
  8.9169-  (:import-from :asdf/component #:%additional-input-files)
  8.9170-  (:export
  8.9171-   #:action #:define-convenience-action-methods
  8.9172-   #:action-description #:format-action
  8.9173-   #:downward-operation #:upward-operation #:sideway-operation #:selfward-operation
  8.9174-   #:non-propagating-operation
  8.9175-   #:component-depends-on
  8.9176-   #:input-files #:output-files #:output-file #:operation-done-p
  8.9177-   #:action-operation #:action-component #:make-action
  8.9178-   #:component-operation-time #:mark-operation-done #:compute-action-stamp
  8.9179-   #:perform #:perform-with-restarts #:retry #:accept
  8.9180-   #:action-path #:find-action
  8.9181-   #:operation-definition-warning #:operation-definition-error ;; condition
  8.9182-   #:action-valid-p
  8.9183-   #:circular-dependency #:circular-dependency-actions
  8.9184-   #:call-while-visiting-action #:while-visiting-action
  8.9185-   #:additional-input-files))
  8.9186-(in-package :asdf/action)
  8.9187-
  8.9188-(eval-when (#-lispworks :compile-toplevel :load-toplevel :execute) ;; LispWorks issues spurious warning
  8.9189-
  8.9190-  (deftype action ()
  8.9191-    "A pair of operation and component uniquely identifies a node in the dependency graph
  8.9192-of steps to be performed while building a system."
  8.9193-    '(cons operation component))
  8.9194-
  8.9195-  (deftype operation-designator ()
  8.9196-    "An operation designates itself. NIL designates a context-dependent current operation,
  8.9197-and a class-name or class designates the canonical instance of the designated class."
  8.9198-    '(or operation null symbol class)))
  8.9199-
  8.9200-;;; these are pseudo accessors -- let us abstract away the CONS cell representation of plan
  8.9201-;;; actions.
  8.9202-(with-upgradability ()
  8.9203-  (defun make-action (operation component)
  8.9204-    (cons operation component))
  8.9205-  (defun action-operation (action)
  8.9206-    (car action))
  8.9207-  (defun action-component (action)
  8.9208-    (cdr action)))
  8.9209-
  8.9210-;;;; Reified representation for storage or debugging. Note: an action is identified by its class.
  8.9211-(with-upgradability ()
  8.9212-  (defun action-path (action)
  8.9213-    "A readable data structure that identifies the action."
  8.9214-    (when action
  8.9215-      (let ((o (action-operation action))
  8.9216-            (c (action-component action)))
  8.9217-        (cons (type-of o) (component-find-path c)))))
  8.9218-  (defun find-action (path)
  8.9219-    "Reconstitute an action from its action-path"
  8.9220-    (destructuring-bind (o . c) path (make-action (make-operation o) (find-component () c)))))
  8.9221-
  8.9222-;;;; Convenience methods
  8.9223-(with-upgradability ()
  8.9224-  ;; A macro that defines convenience methods for a generic function (gf) that
  8.9225-  ;; dispatches on operation and component.  The convenience methods allow users
  8.9226-  ;; to call the gf with operation and/or component designators, that the
  8.9227-  ;; methods will resolve into actual operation and component objects, so that
  8.9228-  ;; the users can interact using readable designators, but developers only have
  8.9229-  ;; to write methods that handle operation and component objects.
  8.9230-  ;; FUNCTION is the generic function name
  8.9231-  ;; FORMALS is its list of arguments, which must include OPERATION and COMPONENT.
  8.9232-  ;; IF-NO-OPERATION is a form (defaults to NIL) describing what to do if no operation is found.
  8.9233-  ;; IF-NO-COMPONENT is a form (defaults to NIL) describing what to do if no component is found.
  8.9234-  (defmacro define-convenience-action-methods
  8.9235-      (function formals &key if-no-operation if-no-component)
  8.9236-    (let* ((rest (gensym "REST"))
  8.9237-           (found (gensym "FOUND"))
  8.9238-           (keyp (equal (last formals) '(&key)))
  8.9239-           (formals-no-key (if keyp (butlast formals) formals))
  8.9240-           (len (length formals-no-key))
  8.9241-           (operation 'operation)
  8.9242-           (component 'component)
  8.9243-           (opix (position operation formals))
  8.9244-           (coix (position component formals))
  8.9245-           (prefix (subseq formals 0 opix))
  8.9246-           (suffix (subseq formals (1+ coix) len))
  8.9247-           (more-args (when keyp `(&rest ,rest &key &allow-other-keys))))
  8.9248-      (assert (and (integerp opix) (integerp coix) (= coix (1+ opix))))
  8.9249-      (flet ((next-method (o c)
  8.9250-               (if keyp
  8.9251-                   `(apply ',function ,@prefix ,o ,c ,@suffix ,rest)
  8.9252-                   `(,function ,@prefix ,o ,c ,@suffix))))
  8.9253-        `(progn
  8.9254-           (defmethod ,function (,@prefix (,operation string) ,component ,@suffix ,@more-args)
  8.9255-             (declare (notinline ,function))
  8.9256-             (let ((,component (find-component () ,component))) ;; do it first, for defsystem-depends-on
  8.9257-               ,(next-method `(safe-read-from-string ,operation :package :asdf/interface) component)))
  8.9258-           (defmethod ,function (,@prefix (,operation symbol) ,component ,@suffix ,@more-args)
  8.9259-             (declare (notinline ,function))
  8.9260-             (if ,operation
  8.9261-                 ,(next-method
  8.9262-                   `(make-operation ,operation)
  8.9263-                   `(or (find-component () ,component) ,if-no-component))
  8.9264-                 ,if-no-operation))
  8.9265-           (defmethod ,function (,@prefix (,operation operation) ,component ,@suffix ,@more-args)
  8.9266-             (declare (notinline ,function))
  8.9267-             (if (typep ,component 'component)
  8.9268-                 (error "No defined method for ~S on ~/asdf-action:format-action/"
  8.9269-                        ',function (make-action ,operation ,component))
  8.9270-                 (if-let (,found (find-component () ,component))
  8.9271-                    ,(next-method operation found)
  8.9272-                    ,if-no-component))))))))
  8.9273-
  8.9274-
  8.9275-;;;; Self-description
  8.9276-(with-upgradability ()
  8.9277-  (defgeneric action-description (operation component)
  8.9278-    (:documentation "returns a phrase that describes performing this operation
  8.9279-on this component, e.g. \"loading /a/b/c\".
  8.9280-You can put together sentences using this phrase."))
  8.9281-  (defmethod action-description (operation component)
  8.9282-    (format nil (compatfmt "~@<~A on ~A~@:>")
  8.9283-            operation component))
  8.9284-
  8.9285-  (defun format-action (stream action &optional colon-p at-sign-p)
  8.9286-    "FORMAT helper to display an action's action-description.
  8.9287-Use it in FORMAT control strings as ~/asdf-action:format-action/"
  8.9288-    (assert (null colon-p)) (assert (null at-sign-p))
  8.9289-    (destructuring-bind (operation . component) action
  8.9290-      (princ (action-description operation component) stream))))
  8.9291-
  8.9292-
  8.9293-;;;; Detection of circular dependencies
  8.9294-(with-upgradability ()
  8.9295-  (defun action-valid-p (operation component)
  8.9296-    "Is this action valid to include amongst dependencies?"
  8.9297-    ;; If either the operation or component was resolved to nil, the action is invalid.
  8.9298-    ;; :if-feature will invalidate actions on components for which the features don't apply.
  8.9299-    (and operation component
  8.9300-         (if-let (it (component-if-feature component)) (featurep it) t)))
  8.9301-
  8.9302-  (define-condition circular-dependency (system-definition-error)
  8.9303-    ((actions :initarg :actions :reader circular-dependency-actions))
  8.9304-    (:report (lambda (c s)
  8.9305-               (format s (compatfmt "~@<Circular dependency of ~s on: ~3i~_~S~@:>")
  8.9306-                       (first (circular-dependency-actions c))
  8.9307-                       (circular-dependency-actions c)))))
  8.9308-
  8.9309-  (defun call-while-visiting-action (operation component fun)
  8.9310-    "Detect circular dependencies"
  8.9311-    (with-asdf-session ()
  8.9312-      (with-accessors ((action-set visiting-action-set)
  8.9313-                       (action-list visiting-action-list)) *asdf-session*
  8.9314-        (let ((action (cons operation component)))
  8.9315-          (when (gethash action action-set)
  8.9316-            (error 'circular-dependency :actions
  8.9317-                   (member action (reverse action-list) :test 'equal)))
  8.9318-          (setf (gethash action action-set) t)
  8.9319-          (push action action-list)
  8.9320-          (unwind-protect
  8.9321-               (funcall fun)
  8.9322-            (pop action-list)
  8.9323-            (setf (gethash action action-set) nil))))))
  8.9324-
  8.9325-  ;; Syntactic sugar for call-while-visiting-action
  8.9326-  (defmacro while-visiting-action ((o c) &body body)
  8.9327-    `(call-while-visiting-action ,o ,c #'(lambda () ,@body))))
  8.9328-
  8.9329-
  8.9330-;;;; Dependencies
  8.9331-(with-upgradability ()
  8.9332-  (defgeneric component-depends-on (operation component) ;; ASDF4: rename to component-dependencies
  8.9333-    (:documentation
  8.9334-     "Returns a list of dependencies needed by the component to perform
  8.9335-    the operation.  A dependency has one of the following forms:
  8.9336-
  8.9337-      (<operation> <component>*), where <operation> is an operation designator
  8.9338-        with respect to FIND-OPERATION in the context of the OPERATION argument,
  8.9339-        and each <component> is a component designator with respect to
  8.9340-        FIND-COMPONENT in the context of the COMPONENT argument,
  8.9341-        and means that the component depends on
  8.9342-        <operation> having been performed on each <component>;
  8.9343-
  8.9344-        [Note: an <operation> is an operation designator -- it can be either an
  8.9345-        operation name or an operation object.  Similarly, a <component> may be
  8.9346-        a component name or a component object.  Also note that, the degenerate
  8.9347-        case of (<operation>) is a no-op.]
  8.9348-
  8.9349-    Methods specialized on subclasses of existing component types
  8.9350-    should usually append the results of CALL-NEXT-METHOD to the list."))
  8.9351-  (define-convenience-action-methods component-depends-on (operation component))
  8.9352-
  8.9353-  (defmethod component-depends-on :around ((o operation) (c component))
  8.9354-    (do-asdf-cache `(component-depends-on ,o ,c)
  8.9355-      (call-next-method))))
  8.9356-
  8.9357-
  8.9358-;;;; upward-operation, downward-operation, sideway-operation, selfward-operation
  8.9359-;; These together handle actions that propagate along the component hierarchy or operation universe.
  8.9360-(with-upgradability ()
  8.9361-  (defclass downward-operation (operation)
  8.9362-    ((downward-operation
  8.9363-      :initform nil :reader downward-operation
  8.9364-      :type operation-designator :allocation :class))
  8.9365-    (:documentation "A DOWNWARD-OPERATION's dependencies propagate down the component hierarchy.
  8.9366-I.e., if O is a DOWNWARD-OPERATION and its DOWNWARD-OPERATION slot designates operation D, then
  8.9367-the action (O . M) of O on module M will depends on each of (D . C) for each child C of module M.
  8.9368-The default value for slot DOWNWARD-OPERATION is NIL, which designates the operation O itself.
  8.9369-E.g. in order for a MODULE to be loaded with LOAD-OP (resp. compiled with COMPILE-OP), all the
  8.9370-children of the MODULE must have been loaded with LOAD-OP (resp. compiled with COMPILE-OP."))
  8.9371-  (defun downward-operation-depends-on (o c)
  8.9372-    `((,(or (downward-operation o) o) ,@(component-children c))))
  8.9373-  (defmethod component-depends-on ((o downward-operation) (c parent-component))
  8.9374-    `(,@(downward-operation-depends-on o c) ,@(call-next-method)))
  8.9375-
  8.9376-  (defclass upward-operation (operation)
  8.9377-    ((upward-operation
  8.9378-      :initform nil :reader upward-operation
  8.9379-      :type operation-designator :allocation :class))
  8.9380-    (:documentation "An UPWARD-OPERATION has dependencies that propagate up the component hierarchy.
  8.9381-I.e., if O is an instance of UPWARD-OPERATION, and its UPWARD-OPERATION slot designates operation U,
  8.9382-then the action (O . C) of O on a component C that has the parent P will depends on (U . P).
  8.9383-The default value for slot UPWARD-OPERATION is NIL, which designates the operation O itself.
  8.9384-E.g. in order for a COMPONENT to be prepared for loading or compiling with PREPARE-OP, its PARENT
  8.9385-must first be prepared for loading or compiling with PREPARE-OP."))
  8.9386-  ;; For backward-compatibility reasons, a system inherits from module and is a child-component
  8.9387-  ;; so we must guard against this case. ASDF4: remove that.
  8.9388-  (defun upward-operation-depends-on (o c)
  8.9389-    (if-let (p (component-parent c)) `((,(or (upward-operation o) o) ,p))))
  8.9390-  (defmethod component-depends-on ((o upward-operation) (c child-component))
  8.9391-    `(,@(upward-operation-depends-on o c) ,@(call-next-method)))
  8.9392-
  8.9393-  (defclass sideway-operation (operation)
  8.9394-    ((sideway-operation
  8.9395-      :initform nil :reader sideway-operation
  8.9396-      :type operation-designator :allocation :class))
  8.9397-    (:documentation "A SIDEWAY-OPERATION has dependencies that propagate \"sideway\" to siblings
  8.9398-that a component depends on. I.e. if O is a SIDEWAY-OPERATION, and its SIDEWAY-OPERATION slot
  8.9399-designates operation S (where NIL designates O itself), then the action (O . C) of O on component C
  8.9400-depends on each of (S . D) where D is a declared dependency of C.
  8.9401-E.g. in order for a COMPONENT to be prepared for loading or compiling with PREPARE-OP,
  8.9402-each of its declared dependencies must first be loaded as by LOAD-OP."))
  8.9403-  (defun sideway-operation-depends-on (o c)
  8.9404-    `((,(or (sideway-operation o) o) ,@(component-sideway-dependencies c))))
  8.9405-  (defmethod component-depends-on ((o sideway-operation) (c component))
  8.9406-    `(,@(sideway-operation-depends-on o c) ,@(call-next-method)))
  8.9407-
  8.9408-  (defclass selfward-operation (operation)
  8.9409-    ((selfward-operation
  8.9410-      ;; NB: no :initform -- if an operation depends on others, it must explicitly specify which
  8.9411-      :type (or operation-designator list) :reader selfward-operation :allocation :class))
  8.9412-    (:documentation "A SELFWARD-OPERATION depends on another operation on the same component.
  8.9413-I.e., if O is a SELFWARD-OPERATION, and its SELFWARD-OPERATION designates a list of operations L,
  8.9414-then the action (O . C) of O on component C depends on each (S . C) for S in L.
  8.9415-E.g. before a component may be loaded by LOAD-OP, it must have been compiled by COMPILE-OP.
  8.9416-A operation-designator designates a singleton list of the designated operation;
  8.9417-a list of operation-designators designates the list of designated operations;
  8.9418-NIL is not a valid operation designator in that context.  Note that any dependency
  8.9419-ordering between the operations in a list of SELFWARD-OPERATION should be specified separately
  8.9420-in the respective operation's COMPONENT-DEPENDS-ON methods so that they be scheduled properly."))
  8.9421-  (defun selfward-operation-depends-on (o c)
  8.9422-    (loop :for op :in (ensure-list (selfward-operation o)) :collect `(,op ,c)))
  8.9423-  (defmethod component-depends-on ((o selfward-operation) (c component))
  8.9424-    `(,@(selfward-operation-depends-on o c) ,@(call-next-method)))
  8.9425-
  8.9426-  (defclass non-propagating-operation (operation)
  8.9427-    ()
  8.9428-    (:documentation "A NON-PROPAGATING-OPERATION is an operation that propagates
  8.9429-no dependencies whatsoever.  It is supplied in order that the programmer be able
  8.9430-to specify that s/he is intentionally specifying an operation which invokes no
  8.9431-dependencies.")))
  8.9432-
  8.9433-
  8.9434-;;;---------------------------------------------------------------------------
  8.9435-;;; Help programmers catch obsolete OPERATION subclasses
  8.9436-;;;---------------------------------------------------------------------------
  8.9437-(with-upgradability ()
  8.9438-  (define-condition operation-definition-warning (simple-warning)
  8.9439-    ()
  8.9440-    (:documentation "Warning condition related to definition of obsolete OPERATION objects."))
  8.9441-
  8.9442-  (define-condition operation-definition-error (simple-error)
  8.9443-    ()
  8.9444-    (:documentation "Error condition related to definition of incorrect OPERATION objects."))
  8.9445-
  8.9446-  (defmethod initialize-instance :before ((o operation) &key)
  8.9447-    (check-operation-constructor)
  8.9448-    (unless (typep o '(or downward-operation upward-operation sideway-operation
  8.9449-                          selfward-operation non-propagating-operation))
  8.9450-      (warn 'operation-definition-warning
  8.9451-            :format-control
  8.9452-            "No dependency propagating scheme specified for operation class ~S.
  8.9453-The class needs to be updated for ASDF 3.1 and specify appropriate propagation mixins."
  8.9454-            :format-arguments (list (type-of o)))))
  8.9455-
  8.9456-  (defmethod initialize-instance :before ((o non-propagating-operation) &key)
  8.9457-    (when (typep o '(or downward-operation upward-operation sideway-operation selfward-operation))
  8.9458-      (error 'operation-definition-error
  8.9459-             :format-control
  8.9460-             "Inconsistent class: ~S
  8.9461-  NON-PROPAGATING-OPERATION is incompatible with propagating operation classes as superclasses."
  8.9462-             :format-arguments
  8.9463-             (list (type-of o)))))
  8.9464-
  8.9465-  (defun backward-compatible-depends-on (o c)
  8.9466-    "DEPRECATED: all subclasses of OPERATION used in ASDF should inherit from one of
  8.9467- DOWNWARD-OPERATION UPWARD-OPERATION SIDEWAY-OPERATION SELFWARD-OPERATION NON-PROPAGATING-OPERATION.
  8.9468- The function BACKWARD-COMPATIBLE-DEPENDS-ON temporarily provides ASDF2 behaviour for those that
  8.9469- don't. In the future this functionality will be removed, and the default will be no propagation."
  8.9470-    (uiop/version::notify-deprecated-function
  8.9471-     (version-deprecation *asdf-version* :style-warning "3.2")
  8.9472-     `(backward-compatible-depends-on :for-operation ,o))
  8.9473-    `(,@(sideway-operation-depends-on o c)
  8.9474-      ,@(when (typep c 'parent-component) (downward-operation-depends-on o c))))
  8.9475-
  8.9476-  (defmethod component-depends-on ((o operation) (c component))
  8.9477-    `(;; Normal behavior, to allow user-specified in-order-to dependencies
  8.9478-      ,@(cdr (assoc (type-of o) (component-in-order-to c)))
  8.9479-        ;; For backward-compatibility with ASDF2, any operation that doesn't specify propagation
  8.9480-        ;; or non-propagation through an appropriate mixin will be downward and sideway.
  8.9481-        ,@(unless (typep o '(or downward-operation upward-operation sideway-operation
  8.9482-                             selfward-operation non-propagating-operation))
  8.9483-            (backward-compatible-depends-on o c))))
  8.9484-
  8.9485-  (defmethod downward-operation ((o operation)) nil)
  8.9486-  (defmethod sideway-operation ((o operation)) nil))
  8.9487-
  8.9488-
  8.9489-;;;---------------------------------------------------------------------------
  8.9490-;;; End of OPERATION class checking
  8.9491-;;;---------------------------------------------------------------------------
  8.9492-
  8.9493-
  8.9494-;;;; Inputs, Outputs, and invisible dependencies
  8.9495-(with-upgradability ()
  8.9496-  (defgeneric output-files (operation component)
  8.9497-    (:documentation "Methods for this function return two values: a list of output files
  8.9498-corresponding to this action, and a boolean indicating if they have already been subjected
  8.9499-to relevant output translations and should not be further translated.
  8.9500-
  8.9501-Methods on PERFORM *must* call this function to determine where their outputs are to be located.
  8.9502-They may rely on the order of the files to discriminate between outputs.
  8.9503-"))
  8.9504-  (defgeneric input-files (operation component)
  8.9505-    (:documentation "A list of input files corresponding to this action.
  8.9506-
  8.9507-Methods on PERFORM *must* call this function to determine where their inputs are located.
  8.9508-They may rely on the order of the files to discriminate between inputs.
  8.9509-"))
  8.9510-  (defgeneric operation-done-p (operation component)
  8.9511-    (:documentation "Returns a boolean which is NIL if the action must be performed (again)."))
  8.9512-  (define-convenience-action-methods output-files (operation component))
  8.9513-  (define-convenience-action-methods input-files (operation component))
  8.9514-  (define-convenience-action-methods operation-done-p (operation component))
  8.9515-
  8.9516-  (defmethod operation-done-p ((o operation) (c component))
  8.9517-    t)
  8.9518-
  8.9519-  ;; Translate output files, unless asked not to. Memoize the result.
  8.9520-  (defmethod output-files :around ((operation t) (component t))
  8.9521-    (do-asdf-cache `(output-files ,operation ,component)
  8.9522-      (values
  8.9523-       (multiple-value-bind (pathnames fixedp) (call-next-method)
  8.9524-         ;; 1- Make sure we have absolute pathnames
  8.9525-         (let* ((directory (pathname-directory-pathname
  8.9526-                            (component-pathname (find-component () component))))
  8.9527-                (absolute-pathnames
  8.9528-                  (loop
  8.9529-                    :for pathname :in pathnames
  8.9530-                    :collect (ensure-absolute-pathname pathname directory))))
  8.9531-           ;; 2- Translate those pathnames as required
  8.9532-           (if fixedp
  8.9533-               absolute-pathnames
  8.9534-               (mapcar *output-translation-function* absolute-pathnames))))
  8.9535-       t)))
  8.9536-  (defmethod output-files ((o operation) (c component))
  8.9537-    nil)
  8.9538-  (defun output-file (operation component)
  8.9539-    "The unique output file of performing OPERATION on COMPONENT"
  8.9540-    (let ((files (output-files operation component)))
  8.9541-      (assert (length=n-p files 1))
  8.9542-      (first files)))
  8.9543-
  8.9544-  (defgeneric additional-input-files (operation component)
  8.9545-    (:documentation "Additional input files for the operation on this
  8.9546-    component.  These are files that are inferred, rather than
  8.9547-    explicitly specified, and these are typically NOT files that
  8.9548-    undergo operations directly.  Instead, they are files that it is
  8.9549-    important for ASDF to know about in order to compute operation times,etc."))
  8.9550-  (define-convenience-action-methods additional-input-files (operation component))
  8.9551-  (defmethod additional-input-files ((op operation) (comp component))
  8.9552-      (cdr (assoc op (%additional-input-files comp))))
  8.9553-
  8.9554-  ;; Memoize input files.
  8.9555-  (defmethod input-files :around (operation component)
  8.9556-    (do-asdf-cache `(input-files ,operation ,component)
  8.9557-      ;; get the additional input files, if any
  8.9558-      (append (call-next-method)
  8.9559-              ;; must come after the first, for other code that
  8.9560-              ;; assumes the first will be the "key" file
  8.9561-              (additional-input-files operation component))))
  8.9562-
  8.9563-  ;; By default an action has no input-files.
  8.9564-  (defmethod input-files ((o operation) (c component))
  8.9565-    nil)
  8.9566-
  8.9567-  ;; An action with a selfward-operation by default gets its input-files from the output-files of
  8.9568-  ;; the actions using selfward-operations it depends on (and the same component),
  8.9569-  ;; or if there are none, on the component-pathname of the component if it's a file
  8.9570-  ;; -- and then on the results of the next-method.
  8.9571-  (defmethod input-files ((o selfward-operation) (c component))
  8.9572-    `(,@(or (loop :for dep-o :in (ensure-list (selfward-operation o))
  8.9573-                  :append (or (output-files dep-o c) (input-files dep-o c)))
  8.9574-            (if-let ((pathname (component-pathname c)))
  8.9575-              (and (file-pathname-p pathname) (list pathname))))
  8.9576-      ,@(call-next-method))))
  8.9577-
  8.9578-
  8.9579-;;;; Done performing
  8.9580-(with-upgradability ()
  8.9581-  ;; ASDF4: hide it behind plan-action-stamp
  8.9582-  (defgeneric component-operation-time (operation component)
  8.9583-    (:documentation "Return the timestamp for when an action was last performed"))
  8.9584-  (defgeneric (setf component-operation-time) (time operation component)
  8.9585-    (:documentation "Update the timestamp for when an action was last performed"))
  8.9586-  (define-convenience-action-methods component-operation-time (operation component))
  8.9587-
  8.9588-  ;; ASDF4: hide it behind (setf plan-action-stamp)
  8.9589-  (defgeneric mark-operation-done (operation component)
  8.9590-    (:documentation "Mark a action as having been just done.
  8.9591-
  8.9592-Updates the action's COMPONENT-OPERATION-TIME to match the COMPUTE-ACTION-STAMP
  8.9593-using the JUST-DONE flag."))
  8.9594-  (defgeneric compute-action-stamp (plan- operation component &key just-done)
  8.9595-    ;; NB: using plan- rather than plan above allows clisp to upgrade from 2.26(!)
  8.9596-    (:documentation "Has this action been successfully done already,
  8.9597-and at what known timestamp has it been done at or will it be done at?
  8.9598-* PLAN is a plan object modelling future effects of actions,
  8.9599-  or NIL to denote what actually happened.
  8.9600-* OPERATION and COMPONENT denote the action.
  8.9601-Takes keyword JUST-DONE:
  8.9602-* JUST-DONE is a boolean that is true if the action was just successfully performed,
  8.9603-  at which point we want compute the actual stamp and warn if files are missing;
  8.9604-  otherwise we are making plans, anticipating the effects of the action.
  8.9605-Returns two values:
  8.9606-* a STAMP saying when it was done or will be done,
  8.9607-  or T if the action involves files that need to be recomputed.
  8.9608-* a boolean DONE-P that indicates whether the action has actually been done,
  8.9609-  and both its output-files and its in-image side-effects are up to date."))
  8.9610-
  8.9611-  (defmethod component-operation-time ((o operation) (c component))
  8.9612-    (gethash o (component-operation-times c)))
  8.9613-
  8.9614-  (defmethod (setf component-operation-time) (stamp (o operation) (c component))
  8.9615-    (assert stamp () "invalid null stamp for ~A" (action-description o c))
  8.9616-    (setf (gethash o (component-operation-times c)) stamp))
  8.9617-
  8.9618-  (defmethod mark-operation-done ((o operation) (c component))
  8.9619-    (let ((stamp (compute-action-stamp nil o c :just-done t)))
  8.9620-      (assert stamp () "Failed to compute a stamp for completed action ~A" (action-description o c))1
  8.9621-      (setf (component-operation-time o c) stamp))))
  8.9622-
  8.9623-
  8.9624-;;;; Perform
  8.9625-(with-upgradability ()
  8.9626-  (defgeneric perform (operation component)
  8.9627-    (:documentation "PERFORM an action, consuming its input-files and building its output-files"))
  8.9628-  (define-convenience-action-methods perform (operation component))
  8.9629-
  8.9630-  (defmethod perform :around ((o operation) (c component))
  8.9631-    (while-visiting-action (o c) (call-next-method)))
  8.9632-  (defmethod perform :before ((o operation) (c component))
  8.9633-    (ensure-all-directories-exist (output-files o c)))
  8.9634-  (defmethod perform :after ((o operation) (c component))
  8.9635-    (mark-operation-done o c))
  8.9636-  (defmethod perform ((o operation) (c parent-component))
  8.9637-    nil)
  8.9638-  (defmethod perform ((o operation) (c source-file))
  8.9639-    ;; For backward compatibility, don't error on operations that don't specify propagation.
  8.9640-    (when (typep o '(or downward-operation upward-operation sideway-operation
  8.9641-                     selfward-operation non-propagating-operation))
  8.9642-      (sysdef-error
  8.9643-       (compatfmt "~@<Required method ~S not implemented for ~/asdf-action:format-action/~@:>")
  8.9644-       'perform (make-action o c))))
  8.9645-
  8.9646-  ;; The restarts of the perform-with-restarts variant matter in an interactive context.
  8.9647-  ;; The retry strategies of p-w-r itself, and/or the background workers of a multiprocess build
  8.9648-  ;; may call perform directly rather than call p-w-r.
  8.9649-  (defgeneric perform-with-restarts (operation component)
  8.9650-    (:documentation "PERFORM an action in a context where suitable restarts are in place."))
  8.9651-  (defmethod perform-with-restarts (operation component)
  8.9652-    (perform operation component))
  8.9653-  (defmethod perform-with-restarts :around (operation component)
  8.9654-    (loop
  8.9655-      (restart-case
  8.9656-          (return (call-next-method))
  8.9657-        (retry ()
  8.9658-          :report
  8.9659-          (lambda (s)
  8.9660-            (format s (compatfmt "~@<Retry ~A.~@:>")
  8.9661-                    (action-description operation component))))
  8.9662-        (accept ()
  8.9663-          :report
  8.9664-          (lambda (s)
  8.9665-            (format s (compatfmt "~@<Continue, treating ~A as having been successful.~@:>")
  8.9666-                    (action-description operation component)))
  8.9667-          (mark-operation-done operation component)
  8.9668-          (return))))))
  8.9669-;;;; -------------------------------------------------------------------------
  8.9670-;;;; Actions to build Common Lisp software
  8.9671-
  8.9672-(uiop/package:define-package :asdf/lisp-action
  8.9673-  (:recycle :asdf/lisp-action :asdf)
  8.9674-  (:use :uiop/common-lisp :uiop :asdf/upgrade :asdf/session
  8.9675-   :asdf/component :asdf/system :asdf/operation :asdf/action)
  8.9676-  (:export
  8.9677-   #:try-recompiling
  8.9678-   #:cl-source-file #:cl-source-file.cl #:cl-source-file.lsp
  8.9679-   #:basic-load-op #:basic-compile-op
  8.9680-   #:load-op #:prepare-op #:compile-op #:test-op #:load-source-op #:prepare-source-op
  8.9681-   #:call-with-around-compile-hook
  8.9682-   #:perform-lisp-compilation #:perform-lisp-load-fasl #:perform-lisp-load-source
  8.9683-   #:lisp-compilation-output-files))
  8.9684-(in-package :asdf/lisp-action)
  8.9685-
  8.9686-
  8.9687-;;;; Component classes
  8.9688-(with-upgradability ()
  8.9689-  (defclass cl-source-file (source-file)
  8.9690-    ((type :initform "lisp"))
  8.9691-    (:documentation "Component class for a Common Lisp source file (using type \"lisp\")"))
  8.9692-  (defclass cl-source-file.cl (cl-source-file)
  8.9693-    ((type :initform "cl"))
  8.9694-    (:documentation "Component class for a Common Lisp source file using type \"cl\""))
  8.9695-  (defclass cl-source-file.lsp (cl-source-file)
  8.9696-    ((type :initform "lsp"))
  8.9697-    (:documentation "Component class for a Common Lisp source file using type \"lsp\"")))
  8.9698-
  8.9699-
  8.9700-;;;; Operation classes
  8.9701-(with-upgradability ()
  8.9702-  (defclass basic-load-op (operation) ()
  8.9703-    (:documentation "Base class for operations that apply the load-time effects of a file"))
  8.9704-  (defclass basic-compile-op (operation) ()
  8.9705-    (:documentation "Base class for operations that apply the compile-time effects of a file")))
  8.9706-
  8.9707-
  8.9708-;;; Our default operations: loading into the current lisp image
  8.9709-(with-upgradability ()
  8.9710-  (defclass prepare-op (upward-operation sideway-operation)
  8.9711-    ((sideway-operation :initform 'load-op :allocation :class))
  8.9712-    (:documentation "Load the dependencies for the COMPILE-OP or LOAD-OP of a given COMPONENT."))
  8.9713-  (defclass load-op (basic-load-op downward-operation selfward-operation)
  8.9714-    ;; NB: even though compile-op depends on prepare-op it is not needed-in-image-p,
  8.9715-    ;; so we need to directly depend on prepare-op for its side-effects in the current image.
  8.9716-    ((selfward-operation :initform '(prepare-op compile-op) :allocation :class))
  8.9717-    (:documentation "Operation for loading the compiled FASL for a Lisp file"))
  8.9718-  (defclass compile-op (basic-compile-op downward-operation selfward-operation)
  8.9719-    ((selfward-operation :initform 'prepare-op :allocation :class))
  8.9720-    (:documentation "Operation for compiling a Lisp file to a FASL"))
  8.9721-
  8.9722-
  8.9723-  (defclass prepare-source-op (upward-operation sideway-operation)
  8.9724-    ((sideway-operation :initform 'load-source-op :allocation :class))
  8.9725-    (:documentation "Operation for loading the dependencies of a Lisp file as source."))
  8.9726-  (defclass load-source-op (basic-load-op downward-operation selfward-operation)
  8.9727-    ((selfward-operation :initform 'prepare-source-op :allocation :class))
  8.9728-    (:documentation "Operation for loading a Lisp file as source."))
  8.9729-
  8.9730-  (defclass test-op (selfward-operation)
  8.9731-    ((selfward-operation :initform 'load-op :allocation :class))
  8.9732-    (:documentation "Operation for running the tests for system.
  8.9733-If the tests fail, an error will be signaled.")))
  8.9734-
  8.9735-
  8.9736-;;;; Methods for prepare-op, compile-op and load-op
  8.9737-
  8.9738-;;; prepare-op
  8.9739-(with-upgradability ()
  8.9740-  (defmethod action-description ((o prepare-op) (c component))
  8.9741-    (format nil (compatfmt "~@<loading dependencies of ~3i~_~A~@:>") c))
  8.9742-  (defmethod perform ((o prepare-op) (c component))
  8.9743-    nil)
  8.9744-  (defmethod input-files ((o prepare-op) (s system))
  8.9745-    (if-let (it (system-source-file s)) (list it))))
  8.9746-
  8.9747-;;; compile-op
  8.9748-(with-upgradability ()
  8.9749-  (defmethod action-description ((o compile-op) (c component))
  8.9750-    (format nil (compatfmt "~@<compiling ~3i~_~A~@:>") c))
  8.9751-  (defmethod action-description ((o compile-op) (c parent-component))
  8.9752-    (format nil (compatfmt "~@<completing compilation for ~3i~_~A~@:>") c))
  8.9753-  (defgeneric call-with-around-compile-hook (component thunk)
  8.9754-    (:documentation "A method to be called around the PERFORM'ing of actions that apply the
  8.9755-compile-time side-effects of file (i.e., COMPILE-OP or LOAD-SOURCE-OP). This method can be used
  8.9756-to setup readtables and other variables that control reading, macroexpanding, and compiling, etc.
  8.9757-Note that it will NOT be called around the performing of LOAD-OP."))
  8.9758-  (defmethod call-with-around-compile-hook ((c component) function)
  8.9759-    (call-around-hook (around-compile-hook c) function))
  8.9760-  (defun perform-lisp-compilation (o c)
  8.9761-    "Perform the compilation of the Lisp file associated to the specified action (O . C)."
  8.9762-    (let (;; Before 2.26.53, that was unfortunately component-pathname. Now,
  8.9763-          ;; we consult input-files, the first of which should be the one to compile-file
  8.9764-          (input-file (first (input-files o c)))
  8.9765-          ;; On some implementations, there are more than one output-file,
  8.9766-          ;; but the first one should always be the primary fasl that gets loaded.
  8.9767-          (outputs (output-files o c)))
  8.9768-      (multiple-value-bind (output warnings-p failure-p)
  8.9769-          (destructuring-bind
  8.9770-              (output-file
  8.9771-               &optional
  8.9772-                 #+(or clasp ecl mkcl) object-file
  8.9773-                 #+clisp lib-file
  8.9774-                 warnings-file &rest rest) outputs
  8.9775-            ;; Allow for extra outputs that are not of type warnings-file
  8.9776-            ;; The way we do it is kludgy. In ASDF4, output-files shall not be positional.
  8.9777-            (declare (ignore rest))
  8.9778-            (when warnings-file
  8.9779-              (unless (equal (pathname-type warnings-file) (warnings-file-type))
  8.9780-                (setf warnings-file nil)))
  8.9781-            (let ((*package* (find-package* '#:common-lisp-user)))
  8.9782-             (call-with-around-compile-hook
  8.9783-              c #'(lambda (&rest flags)
  8.9784-                    (apply 'compile-file* input-file
  8.9785-                           :output-file output-file
  8.9786-                           :external-format (component-external-format c)
  8.9787-                           :warnings-file warnings-file
  8.9788-                           (append
  8.9789-                            #+clisp (list :lib-file lib-file)
  8.9790-                            #+(or clasp ecl mkcl) (list :object-file object-file)
  8.9791-                            flags))))))
  8.9792-        (check-lisp-compile-results output warnings-p failure-p
  8.9793-                                    "~/asdf-action::format-action/" (list (cons o c))))))
  8.9794-  (defun report-file-p (f)
  8.9795-    "Is F a build report file containing, e.g., warnings to check?"
  8.9796-    (equalp (pathname-type f) "build-report"))
  8.9797-  (defun perform-lisp-warnings-check (o c)
  8.9798-    "Check the warnings associated with the dependencies of an action."
  8.9799-    (let* ((expected-warnings-files (remove-if-not #'warnings-file-p (input-files o c)))
  8.9800-           (actual-warnings-files (loop :for w :in expected-warnings-files
  8.9801-                                        :when (get-file-stamp w)
  8.9802-                                          :collect w
  8.9803-                                        :else :do (warn "Missing warnings file ~S while ~A"
  8.9804-                                                        w (action-description o c)))))
  8.9805-      (check-deferred-warnings actual-warnings-files)
  8.9806-      (let* ((output (output-files o c))
  8.9807-             (report (find-if #'report-file-p output)))
  8.9808-        (when report
  8.9809-          (with-open-file (s report :direction :output :if-exists :supersede)
  8.9810-            (format s ":success~%"))))))
  8.9811-  (defmethod perform ((o compile-op) (c cl-source-file))
  8.9812-    (perform-lisp-compilation o c))
  8.9813-  (defun lisp-compilation-output-files (o c)
  8.9814-    "Compute the output-files for compiling the Lisp file for the specified action (O . C),
  8.9815-an OPERATION and a COMPONENT."
  8.9816-    (let* ((i (first (input-files o c)))
  8.9817-           (f (compile-file-pathname
  8.9818-               i #+clasp :output-type #+ecl :type #+(or clasp ecl) :fasl
  8.9819-               #+mkcl :fasl-p #+mkcl t)))
  8.9820-      `(,f ;; the fasl is the primary output, in first position
  8.9821-        #+clasp
  8.9822-        ,@(unless nil ;; was (use-ecl-byte-compiler-p)
  8.9823-            `(,(compile-file-pathname i :output-type :object)))
  8.9824-        #+clisp
  8.9825-        ,@`(,(make-pathname :type "lib" :defaults f))
  8.9826-        #+ecl
  8.9827-        ,@(unless (use-ecl-byte-compiler-p)
  8.9828-            `(,(compile-file-pathname i :type :object)))
  8.9829-        #+mkcl
  8.9830-        ,(compile-file-pathname i :fasl-p nil) ;; object file
  8.9831-        ,@(when (and *warnings-file-type* (not (builtin-system-p (component-system c))))
  8.9832-            `(,(make-pathname :type *warnings-file-type* :defaults f))))))
  8.9833-  (defmethod output-files ((o compile-op) (c cl-source-file))
  8.9834-    (lisp-compilation-output-files o c))
  8.9835-  (defmethod perform ((o compile-op) (c static-file))
  8.9836-    nil)
  8.9837-
  8.9838-  ;; Performing compile-op on a system will check the deferred warnings for the system
  8.9839-  (defmethod perform ((o compile-op) (c system))
  8.9840-    (when (and *warnings-file-type* (not (builtin-system-p c)))
  8.9841-      (perform-lisp-warnings-check o c)))
  8.9842-  (defmethod input-files ((o compile-op) (c system))
  8.9843-    (when (and *warnings-file-type* (not (builtin-system-p c)))
  8.9844-      ;; The most correct way to do it would be to use:
  8.9845-      ;; (collect-dependencies o c :other-systems nil :keep-operation 'compile-op :keep-component 'cl-source-file)
  8.9846-      ;; but it's expensive and we don't care too much about file order or ASDF extensions.
  8.9847-      (loop :for sub :in (sub-components c :type 'cl-source-file)
  8.9848-            :nconc (remove-if-not 'warnings-file-p (output-files o sub)))))
  8.9849-  (defmethod output-files ((o compile-op) (c system))
  8.9850-    (when (and *warnings-file-type* (not (builtin-system-p c)))
  8.9851-      (if-let ((pathname (component-pathname c)))
  8.9852-        (list (subpathname pathname (coerce-filename c) :type "build-report"))))))
  8.9853-
  8.9854-;;; load-op
  8.9855-(with-upgradability ()
  8.9856-  (defmethod action-description ((o load-op) (c cl-source-file))
  8.9857-    (format nil (compatfmt "~@<loading FASL for ~3i~_~A~@:>") c))
  8.9858-  (defmethod action-description ((o load-op) (c parent-component))
  8.9859-    (format nil (compatfmt "~@<completing load for ~3i~_~A~@:>") c))
  8.9860-  (defmethod action-description ((o load-op) (c component))
  8.9861-    (format nil (compatfmt "~@<loading ~3i~_~A~@:>") c))
  8.9862-  (defmethod perform-with-restarts ((o load-op) (c cl-source-file))
  8.9863-    (loop
  8.9864-      (restart-case
  8.9865-          (return (call-next-method))
  8.9866-        (try-recompiling ()
  8.9867-          :report (lambda (s)
  8.9868-                    (format s "Recompile ~a and try loading it again"
  8.9869-                            (component-name c)))
  8.9870-          (perform (find-operation o 'compile-op) c)))))
  8.9871-  (defun perform-lisp-load-fasl (o c)
  8.9872-    "Perform the loading of a FASL associated to specified action (O . C),
  8.9873-an OPERATION and a COMPONENT."
  8.9874-    (if-let (fasl (first (input-files o c)))
  8.9875-            (let ((*package* (find-package '#:common-lisp-user)))
  8.9876-              (load* fasl))))
  8.9877-  (defmethod perform ((o load-op) (c cl-source-file))
  8.9878-    (perform-lisp-load-fasl o c))
  8.9879-  (defmethod perform ((o load-op) (c static-file))
  8.9880-    nil))
  8.9881-
  8.9882-
  8.9883-;;;; prepare-source-op, load-source-op
  8.9884-
  8.9885-;;; prepare-source-op
  8.9886-(with-upgradability ()
  8.9887-  (defmethod action-description ((o prepare-source-op) (c component))
  8.9888-    (format nil (compatfmt "~@<loading source for dependencies of ~3i~_~A~@:>") c))
  8.9889-  (defmethod input-files ((o prepare-source-op) (s system))
  8.9890-    (if-let (it (system-source-file s)) (list it)))
  8.9891-  (defmethod perform ((o prepare-source-op) (c component))
  8.9892-    nil))
  8.9893-
  8.9894-;;; load-source-op
  8.9895-(with-upgradability ()
  8.9896-  (defmethod action-description ((o load-source-op) (c component))
  8.9897-    (format nil (compatfmt "~@<Loading source of ~3i~_~A~@:>") c))
  8.9898-  (defmethod action-description ((o load-source-op) (c parent-component))
  8.9899-    (format nil (compatfmt "~@<Loaded source of ~3i~_~A~@:>") c))
  8.9900-  (defun perform-lisp-load-source (o c)
  8.9901-    "Perform the loading of a Lisp file as associated to specified action (O . C)"
  8.9902-    (call-with-around-compile-hook
  8.9903-     c #'(lambda ()
  8.9904-           (load* (first (input-files o c))
  8.9905-                  :external-format (component-external-format c)))))
  8.9906-
  8.9907-  (defmethod perform ((o load-source-op) (c cl-source-file))
  8.9908-    (perform-lisp-load-source o c))
  8.9909-  (defmethod perform ((o load-source-op) (c static-file))
  8.9910-    nil))
  8.9911-
  8.9912-
  8.9913-;;;; test-op
  8.9914-(with-upgradability ()
  8.9915-  (defmethod perform ((o test-op) (c component))
  8.9916-    nil)
  8.9917-  (defmethod operation-done-p ((o test-op) (c system))
  8.9918-    "Testing a system is _never_ done."
  8.9919-    nil))
  8.9920-;;;; -------------------------------------------------------------------------
  8.9921-;;;; Finding components
  8.9922-
  8.9923-(uiop/package:define-package :asdf/find-component
  8.9924-  (:recycle :asdf/find-component :asdf/find-system :asdf)
  8.9925-  (:use :uiop/common-lisp :uiop :asdf/upgrade :asdf/session
  8.9926-   :asdf/component :asdf/system :asdf/system-registry)
  8.9927-  (:export
  8.9928-   #:find-component
  8.9929-   #:resolve-dependency-name #:resolve-dependency-spec
  8.9930-   #:resolve-dependency-combination
  8.9931-   ;; Conditions
  8.9932-   #:missing-component #:missing-requires #:missing-parent #:missing-component-of-version #:retry
  8.9933-   #:missing-dependency #:missing-dependency-of-version
  8.9934-   #:missing-requires #:missing-parent
  8.9935-   #:missing-required-by #:missing-version))
  8.9936-(in-package :asdf/find-component)
  8.9937-
  8.9938-;;;; Missing component conditions
  8.9939-
  8.9940-(with-upgradability ()
  8.9941-  (define-condition missing-component (system-definition-error)
  8.9942-    ((requires :initform "(unnamed)" :reader missing-requires :initarg :requires)
  8.9943-     (parent :initform nil :reader missing-parent :initarg :parent)))
  8.9944-
  8.9945-  (define-condition missing-component-of-version (missing-component)
  8.9946-    ((version :initform nil :reader missing-version :initarg :version)))
  8.9947-
  8.9948-  (define-condition missing-dependency (missing-component)
  8.9949-    ((required-by :initarg :required-by :reader missing-required-by)))
  8.9950-
  8.9951-  (defmethod print-object ((c missing-dependency) s)
  8.9952-    (format s (compatfmt "~@<~A, required by ~A~@:>")
  8.9953-            (call-next-method c nil) (missing-required-by c)))
  8.9954-
  8.9955-  (define-condition missing-dependency-of-version (missing-dependency
  8.9956-                                                   missing-component-of-version)
  8.9957-    ())
  8.9958-
  8.9959-  (defmethod print-object ((c missing-component) s)
  8.9960-    (format s (compatfmt "~@<Component ~S not found~@[ in ~A~]~@:>")
  8.9961-            (missing-requires c)
  8.9962-            (when (missing-parent c)
  8.9963-              (coerce-name (missing-parent c)))))
  8.9964-
  8.9965-  (defmethod print-object ((c missing-component-of-version) s)
  8.9966-    (format s (compatfmt "~@<Component ~S does not match version ~A~@[ in ~A~]~@:>")
  8.9967-            (missing-requires c)
  8.9968-            (missing-version c)
  8.9969-            (when (missing-parent c)
  8.9970-              (coerce-name (missing-parent c))))))
  8.9971-
  8.9972-
  8.9973-;;;; Finding components
  8.9974-
  8.9975-(with-upgradability ()
  8.9976-  (defgeneric resolve-dependency-combination (component combinator arguments)
  8.9977-    (:documentation "Return a component satisfying the dependency specification (COMBINATOR . ARGUMENTS)
  8.9978-in the context of COMPONENT"))
  8.9979-
  8.9980-  ;; Methods for find-component
  8.9981-
  8.9982-  ;; If the base component is a string, resolve it as a system, then if not nil follow the path.
  8.9983-  (defmethod find-component ((base string) path &key registered)
  8.9984-    (if-let ((s (if registered
  8.9985-                    (registered-system base)
  8.9986-                    (find-system base nil))))
  8.9987-      (find-component s path :registered registered)))
  8.9988-
  8.9989-  ;; If the base component is a symbol, coerce it to a name if not nil, and resolve that.
  8.9990-  ;; If nil, use the path as base if not nil, or else return nil.
  8.9991-  (defmethod find-component ((base symbol) path &key registered)
  8.9992-    (cond
  8.9993-      (base (find-component (coerce-name base) path :registered registered))
  8.9994-      (path (find-component path nil :registered registered))
  8.9995-      (t    nil)))
  8.9996-
  8.9997-  ;; If the base component is a cons cell, resolve its car, and add its cdr to the path.
  8.9998-  (defmethod find-component ((base cons) path &key registered)
  8.9999-    (find-component (car base) (cons (cdr base) path) :registered registered))
 8.10000-
 8.10001-  ;; If the base component is a parent-component and the path a string, find the named child.
 8.10002-  (defmethod find-component ((parent parent-component) (name string) &key registered)
 8.10003-    (declare (ignorable registered))
 8.10004-    (compute-children-by-name parent :only-if-needed-p t)
 8.10005-    (values (gethash name (component-children-by-name parent))))
 8.10006-
 8.10007-  ;; If the path is a symbol, coerce it to a name if non-nil, or else just return the base.
 8.10008-  (defmethod find-component (base (name symbol) &key registered)
 8.10009-    (if name
 8.10010-        (find-component base (coerce-name name) :registered registered)
 8.10011-        base))
 8.10012-
 8.10013-  ;; If the path is a cons, first resolve its car as path, then its cdr.
 8.10014-  (defmethod find-component ((c component) (name cons) &key registered)
 8.10015-    (find-component (find-component c (car name) :registered registered)
 8.10016-                    (cdr name) :registered registered))
 8.10017-
 8.10018-  ;; If the path is a component, return it, disregarding the base.
 8.10019-  (defmethod find-component ((base t) (actual component) &key registered)
 8.10020-    (declare (ignorable registered))
 8.10021-    actual)
 8.10022-
 8.10023-  ;; Resolve dependency NAME in the context of a COMPONENT, with given optional VERSION constraint.
 8.10024-  ;; This (private) function is used below by RESOLVE-DEPENDENCY-SPEC and by the :VERSION spec.
 8.10025-  (defun resolve-dependency-name (component name &optional version)
 8.10026-    (loop
 8.10027-      (restart-case
 8.10028-          (return
 8.10029-            (let ((comp (find-component (component-parent component) name)))
 8.10030-              (unless comp
 8.10031-                (error 'missing-dependency
 8.10032-                       :required-by component
 8.10033-                       :requires name))
 8.10034-              (when version
 8.10035-                (unless (version-satisfies comp version)
 8.10036-                  (error 'missing-dependency-of-version
 8.10037-                         :required-by component
 8.10038-                         :version version
 8.10039-                         :requires name)))
 8.10040-              comp))
 8.10041-        (retry ()
 8.10042-          :report (lambda (s)
 8.10043-                    (format s (compatfmt "~@<Retry loading ~3i~_~A.~@:>") name))
 8.10044-          :test
 8.10045-          (lambda (c)
 8.10046-            (or (null c)
 8.10047-                (and (typep c 'missing-dependency)
 8.10048-                     (eq (missing-required-by c) component)
 8.10049-                     (equal (missing-requires c) name))))
 8.10050-          (unless (component-parent component)
 8.10051-            (let ((name (coerce-name name)))
 8.10052-              (unset-asdf-cache-entry `(find-system ,name))))))))
 8.10053-
 8.10054-  ;; Resolve dependency specification DEP-SPEC in the context of COMPONENT.
 8.10055-  ;; This is notably used by MAP-DIRECT-DEPENDENCIES to process the results of COMPONENT-DEPENDS-ON
 8.10056-  ;; and by PARSE-DEFSYSTEM to process DEFSYSTEM-DEPENDS-ON.
 8.10057-  (defun resolve-dependency-spec (component dep-spec)
 8.10058-    (let ((component (find-component () component)))
 8.10059-      (if (atom dep-spec)
 8.10060-          (resolve-dependency-name component dep-spec)
 8.10061-          (resolve-dependency-combination component (car dep-spec) (cdr dep-spec)))))
 8.10062-
 8.10063-  ;; Methods for RESOLVE-DEPENDENCY-COMBINATION to parse lists as dependency specifications.
 8.10064-  (defmethod resolve-dependency-combination (component combinator arguments)
 8.10065-    (parameter-error (compatfmt "~@<In ~S, bad dependency ~S for ~S~@:>")
 8.10066-                     'resolve-dependency-combination (cons combinator arguments) component))
 8.10067-
 8.10068-  (defmethod resolve-dependency-combination (component (combinator (eql :feature)) arguments)
 8.10069-    (when (featurep (first arguments))
 8.10070-      (resolve-dependency-spec component (second arguments))))
 8.10071-
 8.10072-  (defmethod resolve-dependency-combination (component (combinator (eql :version)) arguments)
 8.10073-    (resolve-dependency-name component (first arguments) (second arguments)))) ;; See lp#527788
 8.10074-
 8.10075-;;;; -------------------------------------------------------------------------
 8.10076-;;;; Forcing
 8.10077-
 8.10078-(uiop/package:define-package :asdf/forcing
 8.10079-  (:recycle :asdf/forcing :asdf/plan :asdf)
 8.10080-  (:use :uiop/common-lisp :uiop :asdf/upgrade :asdf/session
 8.10081-        :asdf/component :asdf/operation :asdf/system :asdf/system-registry)
 8.10082-  (:export
 8.10083-   #:forcing #:make-forcing #:forced #:forced-not #:performable-p
 8.10084-   #:normalize-forced-systems #:normalize-forced-not-systems
 8.10085-   #:action-forced-p #:action-forced-not-p))
 8.10086-(in-package :asdf/forcing)
 8.10087-
 8.10088-;;;; Forcing
 8.10089-(with-upgradability ()
 8.10090-  (defclass forcing ()
 8.10091-    (;; Can plans using this forcing be PERFORMed? A plan that has different force and force-not
 8.10092-     ;; settings than the session can only be used for read-only queries that do not cause the
 8.10093-     ;; status of any action to be raised.
 8.10094-     (performable-p :initform nil :initarg :performable-p :reader performable-p)
 8.10095-     ;; Parameters
 8.10096-     (parameters :initform nil :initarg :parameters :reader parameters)
 8.10097-     ;; Table of systems specified via :force arguments
 8.10098-     (forced :initarg :forced :reader forced)
 8.10099-     ;; Table of systems specified via :force-not argument (and/or immutable)
 8.10100-     (forced-not :initarg :forced-not :reader forced-not)))
 8.10101-
 8.10102-  (defgeneric action-forced-p (forcing operation component)
 8.10103-    (:documentation "Is this action forced to happen in this plan?"))
 8.10104-  (defgeneric action-forced-not-p (forcing operation component)
 8.10105-    (:documentation "Is this action forced to not happen in this plan?
 8.10106-Takes precedence over action-forced-p."))
 8.10107-
 8.10108-  (defun normalize-forced-systems (force system)
 8.10109-    "Given a SYSTEM on which operate is called and the specified FORCE argument,
 8.10110-extract a hash-set of systems that are forced, or a predicate on system names,
 8.10111-or NIL if none are forced, or :ALL if all are."
 8.10112-    (etypecase force
 8.10113-      ((or (member nil :all) hash-table function) force)
 8.10114-      (cons (list-to-hash-set (mapcar #'coerce-name force)))
 8.10115-      ((eql t) (when system (list-to-hash-set (list (coerce-name system)))))))
 8.10116-
 8.10117-  (defun normalize-forced-not-systems (force-not system)
 8.10118-    "Given a SYSTEM on which operate is called, the specified FORCE-NOT argument,
 8.10119-and the set of IMMUTABLE systems, extract a hash-set of systems that are effectively forced-not,
 8.10120-or predicate on system names, or NIL if none are forced, or :ALL if all are."
 8.10121-    (let ((requested
 8.10122-            (etypecase force-not
 8.10123-              ((or (member nil :all) hash-table function) force-not)
 8.10124-              (cons (list-to-hash-set (mapcar #'coerce-name force-not)))
 8.10125-              ((eql t) (if system (let ((name (coerce-name system)))
 8.10126-                                    #'(lambda (x) (not (equal x name))))
 8.10127-                           :all)))))
 8.10128-      (if (and *immutable-systems* requested)
 8.10129-          #'(lambda (x) (or (call-function requested x)
 8.10130-                            (call-function *immutable-systems* x)))
 8.10131-          (or *immutable-systems* requested))))
 8.10132-
 8.10133-  ;; TODO: shouldn't we be looking up the primary system name, rather than the system name?
 8.10134-  (defun action-override-p (forcing operation component override-accessor)
 8.10135-    "Given a plan, an action, and a function that given the plan accesses a set of overrides,
 8.10136-i.e. force or force-not, see if the override applies to the current action."
 8.10137-    (declare (ignore operation))
 8.10138-    (call-function (funcall override-accessor forcing)
 8.10139-                   (coerce-name (component-system (find-component () component)))))
 8.10140-
 8.10141-  (defmethod action-forced-p (forcing operation component)
 8.10142-    (and
 8.10143-     ;; Did the user ask us to re-perform the action?
 8.10144-     (action-override-p forcing operation component 'forced)
 8.10145-     ;; You really can't force a builtin system and :all doesn't apply to it.
 8.10146-     (not (builtin-system-p (component-system component)))))
 8.10147-
 8.10148-  (defmethod action-forced-not-p (forcing operation component)
 8.10149-    ;; Did the user ask us to not re-perform the action?
 8.10150-    ;; NB: force-not takes precedence over force, as it should
 8.10151-    (action-override-p forcing operation component 'forced-not))
 8.10152-
 8.10153-  ;; Null forcing means no forcing either way
 8.10154-  (defmethod action-forced-p ((forcing null) (operation operation) (component component))
 8.10155-    nil)
 8.10156-  (defmethod action-forced-not-p ((forcing null) (operation operation) (component component))
 8.10157-    nil)
 8.10158-
 8.10159-  (defun or-function (fun1 fun2)
 8.10160-    (cond
 8.10161-      ((or (null fun2) (eq fun1 :all)) fun1)
 8.10162-      ((or (null fun1) (eq fun2 :all)) fun2)
 8.10163-      (t #'(lambda (x) (or (call-function fun1 x) (call-function fun2 x))))))
 8.10164-
 8.10165-  (defun make-forcing (&key performable-p system
 8.10166-                         (force nil force-p) (force-not nil force-not-p) &allow-other-keys)
 8.10167-    (let* ((session-forcing (when *asdf-session* (forcing *asdf-session*)))
 8.10168-           (system (and system (coerce-name system)))
 8.10169-           (forced (normalize-forced-systems force system))
 8.10170-           (forced-not (normalize-forced-not-systems force-not system))
 8.10171-           (parameters `(,@(when force `(:force ,force))
 8.10172-                         ,@(when force-not `(:force-not ,force-not))
 8.10173-                         ,@(when (or (eq force t) (eq force-not t)) `(:system ,system))
 8.10174-                         ,@(when performable-p `(:performable-p t))))
 8.10175-           forcing)
 8.10176-      (cond
 8.10177-        ((not session-forcing)
 8.10178-         (setf forcing (make-instance 'forcing
 8.10179-                                      :performable-p performable-p :parameters parameters
 8.10180-                                      :forced forced :forced-not forced-not))
 8.10181-         (when (and performable-p *asdf-session*)
 8.10182-           (setf (forcing *asdf-session*) forcing)))
 8.10183-        (performable-p
 8.10184-         (when (and (not (equal parameters (parameters session-forcing)))
 8.10185-                    (or force-p force-not-p))
 8.10186-           (parameter-error "~*~S and ~S arguments not allowed in a nested call to ~3:*~S ~
 8.10187-unless identically to toplevel"
 8.10188-                            (find-symbol* :operate :asdf) :force :force-not))
 8.10189-         (setf forcing session-forcing))
 8.10190-        (t
 8.10191-         (setf forcing (make-instance 'forcing
 8.10192-                           ;; Combine force and force-not with values from the toplevel-plan
 8.10193-                           :parameters `(,@parameters :on-top-of ,(parameters session-forcing))
 8.10194-                           :forced (or-function (forced session-forcing) forced)
 8.10195-                           :forced-not (or-function (forced-not session-forcing) forced-not)))))
 8.10196-      forcing))
 8.10197-
 8.10198-  (defmethod print-object ((forcing forcing) stream)
 8.10199-    (print-unreadable-object (forcing stream :type t)
 8.10200-      (format stream "~{~S~^ ~}" (parameters forcing))))
 8.10201-
 8.10202-  ;; During upgrade, the *asdf-session* may legitimately be NIL, so we must handle that case.
 8.10203-  (defmethod forcing ((x null))
 8.10204-    (if-let (session (toplevel-asdf-session))
 8.10205-      (forcing session)
 8.10206-      (make-forcing :performable-p t)))
 8.10207-
 8.10208-  ;; When performing a plan that is a list of actions, use the toplevel asdf sesssion forcing.
 8.10209-  (defmethod forcing ((x cons)) (forcing (toplevel-asdf-session))))
 8.10210-;;;; -------------------------------------------------------------------------
 8.10211-;;;; Plan
 8.10212-
 8.10213-(uiop/package:define-package :asdf/plan
 8.10214-  ;; asdf/action below is needed for required-components, traverse-action and traverse-sub-actions
 8.10215-  ;; that used to live there before 3.2.0.
 8.10216-  (:recycle :asdf/plan :asdf/action :asdf)
 8.10217-  (:use :uiop/common-lisp :uiop :asdf/upgrade :asdf/session
 8.10218-        :asdf/component :asdf/operation :asdf/action :asdf/lisp-action
 8.10219-        :asdf/system :asdf/system-registry :asdf/find-component :asdf/forcing)
 8.10220-  (:export
 8.10221-   #:plan #:plan-traversal #:sequential-plan #:*plan-class*
 8.10222-   #:action-status #:status-stamp #:status-index #:status-done-p #:status-keep-p #:status-need-p
 8.10223-   #:action-already-done-p
 8.10224-   #:+status-good+ #:+status-todo+ #:+status-void+
 8.10225-   #:system-out-of-date #:action-up-to-date-p
 8.10226-   #:circular-dependency #:circular-dependency-actions
 8.10227-   #:needed-in-image-p
 8.10228-   #:map-direct-dependencies #:reduce-direct-dependencies #:direct-dependencies
 8.10229-   #:compute-action-stamp #:traverse-action #:record-dependency
 8.10230-   #:make-plan #:plan-actions #:plan-actions-r #:perform-plan #:mark-as-done
 8.10231-   #:required-components #:filtered-sequential-plan
 8.10232-   #:plan-component-type #:plan-keep-operation #:plan-keep-component))
 8.10233-(in-package :asdf/plan)
 8.10234-
 8.10235-;;;; Generic plan traversal class
 8.10236-(with-upgradability ()
 8.10237-  (defclass plan () ()
 8.10238-    (:documentation "Base class for a plan based on which ASDF can build a system"))
 8.10239-  (defclass plan-traversal (plan)
 8.10240-    (;; The forcing parameters for this plan. Also indicates whether the plan is performable,
 8.10241-     ;; in which case the forcing is the same as for the entire session.
 8.10242-     (forcing :initform (forcing (toplevel-asdf-session)) :initarg :forcing :reader forcing))
 8.10243-    (:documentation "Base class for plans that simply traverse dependencies"))
 8.10244-  ;; Sequential plans (the default)
 8.10245-  (defclass sequential-plan (plan-traversal)
 8.10246-    ((actions-r :initform nil :accessor plan-actions-r))
 8.10247-    (:documentation "Simplest, default plan class, accumulating a sequence of actions"))
 8.10248-
 8.10249-  (defgeneric plan-actions (plan)
 8.10250-    (:documentation "Extract from a plan a list of actions to perform in sequence"))
 8.10251-  (defmethod plan-actions ((plan list))
 8.10252-    plan)
 8.10253-  (defmethod plan-actions ((plan sequential-plan))
 8.10254-    (reverse (plan-actions-r plan)))
 8.10255-
 8.10256-  (defgeneric record-dependency (plan operation component)
 8.10257-    (:documentation "Record that, within PLAN, performing OPERATION on COMPONENT depends on all
 8.10258-of the (OPERATION . COMPONENT) actions in the current ASDF session's VISITING-ACTION-LIST.
 8.10259-
 8.10260-You can get a single action which dominates the set of dependencies corresponding to this call with
 8.10261-(first (visiting-action-list *asdf-session*))
 8.10262-since VISITING-ACTION-LIST is a stack whose top action depends directly on its second action,
 8.10263-and whose second action depends directly on its third action, and so forth."))
 8.10264-
 8.10265-  ;; No need to record a dependency to build a full graph, just accumulate nodes in order.
 8.10266-  (defmethod record-dependency ((plan sequential-plan) (o operation) (c component))
 8.10267-    (values)))
 8.10268-
 8.10269-(when-upgrading (:version "3.3.0")
 8.10270-  (defmethod initialize-instance :after ((plan plan-traversal) &key &allow-other-keys)))
 8.10271-
 8.10272-
 8.10273-;;;; Planned action status
 8.10274-(with-upgradability ()
 8.10275-  (defclass action-status ()
 8.10276-    ((bits
 8.10277-      :type fixnum :initarg :bits :reader status-bits
 8.10278-      :documentation "bitmap describing the status of the action.")
 8.10279-     (stamp
 8.10280-      :type (or integer boolean) :initarg :stamp :reader status-stamp
 8.10281-      :documentation "STAMP associated with the ACTION if it has been completed already in some
 8.10282-previous session or image, T if it was done and builtin the image, or NIL if it needs to be done.")
 8.10283-     (level
 8.10284-      :type fixnum :initarg :level :initform 0 :reader status-level
 8.10285-      :documentation "the highest (operate-level) at which the action was needed")
 8.10286-     (index
 8.10287-      :type (or integer null) :initarg :index :initform nil :reader status-index
 8.10288-      :documentation "INDEX associated with the ACTION in the current session,
 8.10289-or NIL if no the status is considered outside of a specific plan."))
 8.10290-    (:documentation "Status of an action in a plan"))
 8.10291-
 8.10292-  ;; STAMP   KEEP-P DONE-P NEED-P     symbol bitmap  previously   currently
 8.10293-  ;; not-nil   T      T      T     =>  GOOD     7    up-to-date   done (e.g. file previously loaded)
 8.10294-  ;; not-nil   T      T     NIL    =>  HERE     6    up-to-date   unplanned yet done
 8.10295-  ;; not-nil   T     NIL     T     =>  REDO     5    up-to-date   planned (e.g. file to load)
 8.10296-  ;; not-nil   T     NIL    NIL    =>  SKIP     4    up-to-date   unplanned (e.g. file compiled)
 8.10297-  ;; not-nil  NIL     T      T     =>  DONE     3    out-of-date  done
 8.10298-  ;; not-nil  NIL     T     NIL    =>  WHAT     2    out-of-date  unplanned yet done(?)
 8.10299-  ;;  NIL     NIL    NIL     T     =>  TODO     1    out-of-date  planned
 8.10300-  ;;  NIL     NIL    NIL    NIL    =>  VOID     0    out-of-date  unplanned
 8.10301-  ;;
 8.10302-  ;; Note that a VOID status cannot happen as part of a transitive dependency of a wanted node
 8.10303-  ;; while traversing a node with TRAVERSE-ACTION; it can only happen while checking whether an
 8.10304-  ;; action is up-to-date with ACTION-UP-TO-DATE-P.
 8.10305-  ;;
 8.10306-  ;; When calling TRAVERSE-ACTION, the +need-bit+ is set,
 8.10307-  ;; unless the action is up-to-date and not needed-in-image (HERE, SKIP).
 8.10308-  ;; When PERFORMing an action, the +done-bit+ is set.
 8.10309-  ;; When the +need-bit+ is set but not the +done-bit+, the level slot indicates which level of
 8.10310-  ;; OPERATE it was last marked needed for; if it happens to be needed at a higher-level, then
 8.10311-  ;; its urgency (and that of its transitive dependencies) must be escalated so that it will be
 8.10312-  ;; done before the end of this level of operate.
 8.10313-  ;;
 8.10314-  ;; Also, when no ACTION-STATUS is associated to an action yet, NIL serves as a bottom value.
 8.10315-  ;;
 8.10316-  (defparameter +keep-bit+ 4)
 8.10317-  (defparameter +done-bit+ 2)
 8.10318-  (defparameter +need-bit+ 1)
 8.10319-  (defparameter +good-bits+ 7)
 8.10320-  (defparameter +todo-bits+ 1)
 8.10321-  (defparameter +void-bits+ 0)
 8.10322-
 8.10323-  (defparameter +status-good+
 8.10324-    (make-instance 'action-status :bits +good-bits+ :stamp t))
 8.10325-  (defparameter +status-todo+
 8.10326-    (make-instance 'action-status :bits +todo-bits+ :stamp nil))
 8.10327-  (defparameter +status-void+
 8.10328-    (make-instance 'action-status :bits +void-bits+ :stamp nil)))
 8.10329-
 8.10330-(with-upgradability ()
 8.10331-  (defun make-action-status (&key bits stamp (level 0) index)
 8.10332-    (check-type bits (integer 0 7))
 8.10333-    (check-type stamp (or integer boolean))
 8.10334-    (check-type level (integer 0 #.most-positive-fixnum))
 8.10335-    (check-type index (or integer null))
 8.10336-    (assert (eq (null stamp) (zerop (logand bits #.(logior +keep-bit+ +done-bit+)))) ()
 8.10337-            "Bad action-status :bits ~S :stamp ~S" bits stamp)
 8.10338-    (block nil
 8.10339-      (when (and (null index) (zerop level))
 8.10340-        (case bits
 8.10341-          (#.+void-bits+ (return +status-void+))
 8.10342-          (#.+todo-bits+ (return +status-todo+))
 8.10343-          (#.+good-bits+ (when (eq stamp t) (return +status-good+)))))
 8.10344-      (make-instance 'action-status :bits bits :stamp stamp :level level :index index)))
 8.10345-
 8.10346-  (defun status-keep-p (status)
 8.10347-    (plusp (logand (status-bits status) #.+keep-bit+)))
 8.10348-  (defun status-done-p (status)
 8.10349-    (plusp (logand (status-bits status) #.+done-bit+)))
 8.10350-  (defun status-need-p (status)
 8.10351-    (plusp (logand (status-bits status) #.+need-bit+)))
 8.10352-
 8.10353-  (defun merge-action-status (status1 status2) ;; status-and
 8.10354-    "Return the earliest status later than both status1 and status2"
 8.10355-    (make-action-status
 8.10356-     :bits (logand (status-bits status1) (status-bits status2))
 8.10357-     :stamp (latest-timestamp (status-stamp status1) (status-stamp status2))
 8.10358-     :level (min (status-level status1) (status-level status2))
 8.10359-     :index (or (status-index status1) (status-index status2))))
 8.10360-
 8.10361-  (defun mark-status-needed (status &optional (level (operate-level))) ;; limited status-or
 8.10362-    "Return the same status but with the need bit set, for the given level"
 8.10363-    (if (and (status-need-p status)
 8.10364-             (>= (status-level status) level))
 8.10365-        status
 8.10366-        (make-action-status
 8.10367-         :bits (logior (status-bits status) +need-bit+)
 8.10368-         :level (max level (status-level status))
 8.10369-         :stamp (status-stamp status)
 8.10370-         :index (status-index status))))
 8.10371-
 8.10372-  (defmethod print-object ((status action-status) stream)
 8.10373-    (print-unreadable-object (status stream :type t)
 8.10374-      (with-slots (bits stamp level index) status
 8.10375-        (format stream "~{~S~^ ~}" `(:bits ,bits :stamp ,stamp :level ,level :index ,index)))))
 8.10376-
 8.10377-  (defgeneric action-status (plan operation component)
 8.10378-    (:documentation "Returns the ACTION-STATUS associated to the action of OPERATION on COMPONENT
 8.10379-in the PLAN, or NIL if the action wasn't visited yet as part of the PLAN."))
 8.10380-
 8.10381-  (defgeneric (setf action-status) (new-status plan operation component)
 8.10382-    (:documentation "Sets the ACTION-STATUS associated to
 8.10383-the action of OPERATION on COMPONENT in the PLAN"))
 8.10384-
 8.10385-  (defmethod action-status ((plan null) (o operation) (c component))
 8.10386-    (multiple-value-bind (stamp done-p) (component-operation-time o c)
 8.10387-      (if done-p
 8.10388-          (make-action-status :bits #.+keep-bit+ :stamp stamp)
 8.10389-          +status-void+)))
 8.10390-
 8.10391-  (defmethod (setf action-status) (new-status (plan null) (o operation) (c component))
 8.10392-    (let ((times (component-operation-times c)))
 8.10393-      (if (status-done-p new-status)
 8.10394-          (setf (gethash o times) (status-stamp new-status))
 8.10395-          (remhash o times)))
 8.10396-    new-status)
 8.10397-
 8.10398-  ;; Handle FORCED-NOT: it makes an action return its current timestamp as status
 8.10399-  (defmethod action-status ((p plan) (o operation) (c component))
 8.10400-    ;; TODO: should we instead test something like:
 8.10401-    ;; (action-forced-not-p plan operation (primary-system component))
 8.10402-    (or (gethash (make-action o c) (visited-actions *asdf-session*))
 8.10403-        (when (action-forced-not-p (forcing p) o c)
 8.10404-          (let ((status (action-status nil o c)))
 8.10405-            (setf (gethash (make-action o c) (visited-actions *asdf-session*))
 8.10406-                  (make-action-status
 8.10407-                   :bits +good-bits+
 8.10408-                   :stamp (or (and status (status-stamp status)) t)
 8.10409-                   :index (incf (total-action-count *asdf-session*))))))))
 8.10410-
 8.10411-  (defmethod (setf action-status) (new-status (p plan) (o operation) (c component))
 8.10412-    (setf (gethash (make-action o c) (visited-actions *asdf-session*)) new-status))
 8.10413-
 8.10414-  (defmethod (setf action-status) :after
 8.10415-      (new-status (p sequential-plan) (o operation) (c component))
 8.10416-    (unless (status-done-p new-status)
 8.10417-      (push (make-action o c) (plan-actions-r p)))))
 8.10418-
 8.10419-
 8.10420-;;;; Is the action needed in this image?
 8.10421-(with-upgradability ()
 8.10422-  (defgeneric needed-in-image-p (operation component)
 8.10423-    (:documentation "Is the action of OPERATION on COMPONENT needed in the current image
 8.10424-to be meaningful, or could it just as well have been done in another Lisp image?"))
 8.10425-
 8.10426-  (defmethod needed-in-image-p ((o operation) (c component))
 8.10427-    ;; We presume that actions that modify the filesystem don't need be run
 8.10428-    ;; in the current image if they have already been done in another,
 8.10429-    ;; and can be run in another process (e.g. a fork),
 8.10430-    ;; whereas those that don't are meant to side-effect the current image and can't.
 8.10431-    (not (output-files o c))))
 8.10432-
 8.10433-
 8.10434-;;;; Visiting dependencies of an action and computing action stamps
 8.10435-(with-upgradability ()
 8.10436-  (defun map-direct-dependencies (operation component fun)
 8.10437-    "Call FUN on all the valid dependencies of the given action in the given plan"
 8.10438-    (loop :for (dep-o-spec . dep-c-specs) :in (component-depends-on operation component)
 8.10439-          :for dep-o = (find-operation operation dep-o-spec)
 8.10440-          :when dep-o
 8.10441-            :do (loop :for dep-c-spec :in dep-c-specs
 8.10442-                      :for dep-c = (and dep-c-spec (resolve-dependency-spec component dep-c-spec))
 8.10443-                      :when (action-valid-p dep-o dep-c)
 8.10444-                        :do (funcall fun dep-o dep-c))))
 8.10445-
 8.10446-  (defun reduce-direct-dependencies (operation component combinator seed)
 8.10447-    "Reduce the direct dependencies to a value computed by iteratively calling COMBINATOR
 8.10448-for each dependency action on the dependency's operation and component and an accumulator
 8.10449-initialized with SEED."
 8.10450-    (map-direct-dependencies
 8.10451-     operation component
 8.10452-     #'(lambda (dep-o dep-c) (setf seed (funcall combinator dep-o dep-c seed))))
 8.10453-    seed)
 8.10454-
 8.10455-  (defun direct-dependencies (operation component)
 8.10456-    "Compute a list of the direct dependencies of the action within the plan"
 8.10457-    (reverse (reduce-direct-dependencies operation component #'acons nil)))
 8.10458-
 8.10459-  ;; In a distant future, get-file-stamp, component-operation-time and latest-stamp
 8.10460-  ;; shall also be parametrized by the plan, or by a second model object,
 8.10461-  ;; so they need not refer to the state of the filesystem,
 8.10462-  ;; and the stamps could be cryptographic checksums rather than timestamps.
 8.10463-  ;; Such a change remarkably would only affect COMPUTE-ACTION-STAMP.
 8.10464-  (define-condition dependency-not-done (warning)
 8.10465-    ((op
 8.10466-      :initarg :op)
 8.10467-     (component
 8.10468-      :initarg :component)
 8.10469-     (dep-op
 8.10470-      :initarg :dep-op)
 8.10471-     (dep-component
 8.10472-      :initarg :dep-component)
 8.10473-     (plan
 8.10474-      :initarg :plan
 8.10475-      :initform nil))
 8.10476-    (:report (lambda (condition stream)
 8.10477-               (with-slots (op component dep-op dep-component plan) condition
 8.10478-                 (format stream "Computing just-done stamp ~@[in plan ~S~] for action ~S, but dependency ~S wasn't done yet!"
 8.10479-                         plan
 8.10480-                         (action-path (make-action op component))
 8.10481-                         (action-path (make-action dep-op dep-component)))))))
 8.10482-
 8.10483-  (defmethod compute-action-stamp (plan (o operation) (c component) &key just-done)
 8.10484-    ;; Given an action, figure out at what time in the past it has been done,
 8.10485-    ;; or if it has just been done, return the time that it has.
 8.10486-    ;; Returns two values:
 8.10487-    ;; 1- the TIMESTAMP of the action if it has already been done and is up to date,
 8.10488-    ;;   or NIL is either hasn't been done or is out of date.
 8.10489-    ;;   (An ASDF extension could use a cryptographic digest instead.)
 8.10490-    ;; 2- the DONE-IN-IMAGE-P boolean flag that is T if the action has already been done
 8.10491-    ;;   in the current image, or NIL if it hasn't.
 8.10492-    ;; Note that if e.g. LOAD-OP only depends on up-to-date files, but
 8.10493-    ;; hasn't been done in the current image yet, then it can have a non-NIL timestamp,
 8.10494-    ;; yet a NIL done-in-image-p flag: we can predict what timestamp it will have once loaded,
 8.10495-    ;; i.e. that of the input-files.
 8.10496-    ;; If just-done is NIL, these values return are the notional fields of
 8.10497-    ;; a KEEP, REDO or TODO status (VOID is possible, but probably an error).
 8.10498-    ;; If just-done is T, they are the notional fields of DONE status
 8.10499-    ;; (or, if something went wrong, TODO).
 8.10500-    (nest
 8.10501-     (block ())
 8.10502-     (let* ((dep-status ; collect timestamp from dependencies (or T if forced or out-of-date)
 8.10503-             (reduce-direct-dependencies
 8.10504-              o c
 8.10505-              #'(lambda (do dc status)
 8.10506-                  ;; out-of-date dependency: don't bother looking further
 8.10507-                  (let ((action-status (action-status plan do dc)))
 8.10508-                    (cond
 8.10509-                      ((and action-status (or (status-keep-p action-status)
 8.10510-                                              (and just-done (status-stamp action-status))))
 8.10511-                       (merge-action-status action-status status))
 8.10512-                      (just-done
 8.10513-                       ;; It's OK to lose some ASDF action stamps during self-upgrade
 8.10514-                       (unless (equal "asdf" (primary-system-name dc))
 8.10515-                         (warn 'dependency-not-done
 8.10516-                               :plan plan
 8.10517-                               :op o :component c
 8.10518-                               :dep-op do :dep-component dc))
 8.10519-                       status)
 8.10520-                      (t
 8.10521-                       (return (values nil nil))))))
 8.10522-              +status-good+))
 8.10523-            (dep-stamp (status-stamp dep-status))))
 8.10524-     (let* (;; collect timestamps from inputs, and exit early if any is missing
 8.10525-            (in-files (input-files o c))
 8.10526-            (in-stamps (mapcar #'get-file-stamp in-files))
 8.10527-            (missing-in (loop :for f :in in-files :for s :in in-stamps :unless s :collect f))
 8.10528-            (latest-in (timestamps-latest (cons dep-stamp in-stamps))))
 8.10529-       (when (and missing-in (not just-done)) (return (values nil nil))))
 8.10530-     (let* (;; collect timestamps from outputs, and exit early if any is missing
 8.10531-            (out-files (remove-if 'null (output-files o c)))
 8.10532-            (out-stamps (mapcar (if just-done 'register-file-stamp 'get-file-stamp) out-files))
 8.10533-            (missing-out (loop :for f :in out-files :for s :in out-stamps :unless s :collect f))
 8.10534-            (earliest-out (timestamps-earliest out-stamps)))
 8.10535-       (when (and missing-out (not just-done)) (return (values nil nil))))
 8.10536-     (let (;; Time stamps from the files at hand, and whether any is missing
 8.10537-           (all-present (not (or missing-in missing-out)))
 8.10538-           ;; Has any input changed since we last generated the files?
 8.10539-           ;; Note that we use timestamp<= instead of timestamp< to play nice with generated files.
 8.10540-           ;; Any race condition is intrinsic to the limited timestamp resolution.
 8.10541-           (up-to-date-p (timestamp<= latest-in earliest-out))
 8.10542-           ;; If everything is up to date, the latest of inputs and outputs is our stamp
 8.10543-           (done-stamp (timestamps-latest (cons latest-in out-stamps))))
 8.10544-       ;; Warn if some files are missing:
 8.10545-       ;; either our model is wrong or some other process is messing with our files.
 8.10546-       (when (and just-done (not all-present))
 8.10547-         ;; Shouldn't that be an error instead?
 8.10548-         (warn "~A completed without ~:[~*~;~*its input file~:p~2:*~{ ~S~}~*~]~
 8.10549-                ~:[~; or ~]~:[~*~;~*its output file~:p~2:*~{ ~S~}~*~]"
 8.10550-               (action-description o c)
 8.10551-               missing-in (length missing-in) (and missing-in missing-out)
 8.10552-               missing-out (length missing-out))))
 8.10553-     (let (;; There are three kinds of actions:
 8.10554-           (out-op (and out-files t)) ; those that create files on the filesystem
 8.10555-           ;;(image-op (and in-files (null out-files))) ; those that load stuff into the image
 8.10556-           ;;(null-op (and (null out-files) (null in-files))) ; placeholders that do nothing
 8.10557-           ))
 8.10558-     (if (or just-done ;; The done-stamp is valid: if we're just done, or
 8.10559-             (and all-present ;; if all filesystem effects are up-to-date
 8.10560-                  up-to-date-p
 8.10561-                  (operation-done-p o c) ;; and there's no invalidating reason.
 8.10562-                  (not (action-forced-p (forcing (or plan *asdf-session*)) o c))))
 8.10563-         (values done-stamp ;; return the hard-earned timestamp
 8.10564-                 (or just-done
 8.10565-                     out-op ;; A file-creating op is done when all files are up to date.
 8.10566-                     ;; An image-effecting operation is done when
 8.10567-                     (and (status-done-p dep-status) ;; all the dependencies were done, and
 8.10568-                          (multiple-value-bind (perform-stamp perform-done-p)
 8.10569-                              (component-operation-time o c)
 8.10570-                            (and perform-done-p ;; the op was actually run,
 8.10571-                                 (equal perform-stamp done-stamp)))))) ;; with a matching stamp.
 8.10572-         ;; done-stamp invalid: return a timestamp in an indefinite future, action not done yet
 8.10573-         (values nil nil)))))
 8.10574-
 8.10575-
 8.10576-;;;; The four different actual traversals:
 8.10577-;; * TRAVERSE-ACTION o c T: Ensure all dependencies are either up-to-date in-image, or planned
 8.10578-;; * TRAVERSE-ACTION o c NIL: Ensure all dependencies are up-to-date or planned, in-image or not
 8.10579-;; * ACTION-UP-TO-DATE-P: Check whether some (defsystem-depends-on ?) dependencies are up to date
 8.10580-;; * COLLECT-ACTION-DEPENDENCIES: Get the dependencies (filtered), don't change any status
 8.10581-(with-upgradability ()
 8.10582-
 8.10583-  ;; Compute the action status for a newly visited action.
 8.10584-  (defun compute-action-status (plan operation component need-p)
 8.10585-    (multiple-value-bind (stamp done-p)
 8.10586-        (compute-action-stamp plan operation component)
 8.10587-      (assert (or stamp (not done-p)))
 8.10588-      (make-action-status
 8.10589-       :bits (logior (if stamp #.+keep-bit+ 0)
 8.10590-                     (if done-p #.+done-bit+ 0)
 8.10591-                     (if need-p #.+need-bit+ 0))
 8.10592-       :stamp stamp
 8.10593-       :level (operate-level)
 8.10594-       :index (incf (total-action-count *asdf-session*)))))
 8.10595-
 8.10596-  ;; TRAVERSE-ACTION, in the context of a given PLAN object that accumulates dependency data,
 8.10597-  ;; visits the action defined by its OPERATION and COMPONENT arguments,
 8.10598-  ;; and all its transitive dependencies (unless already visited),
 8.10599-  ;; in the context of the action being (or not) NEEDED-IN-IMAGE-P,
 8.10600-  ;; i.e. needs to be done in the current image vs merely have been done in a previous image.
 8.10601-  ;;
 8.10602-  ;; TRAVERSE-ACTION updates the VISITED-ACTIONS entries for the action and for all its
 8.10603-  ;; transitive dependencies (that haven't been sufficiently visited so far).
 8.10604-  ;; It does not return any usable value.
 8.10605-  ;;
 8.10606-  ;; Note that for an XCVB-like plan with one-image-per-file-outputting-action,
 8.10607-  ;; the below method would be insufficient, since it assumes a single image
 8.10608-  ;; to traverse each node at most twice; non-niip actions would be traversed only once,
 8.10609-  ;; but niip nodes could be traversed once per image, i.e. once plus once per non-niip action.
 8.10610-
 8.10611-  (defun traverse-action (plan operation component needed-in-image-p)
 8.10612-    (block nil
 8.10613-      (unless (action-valid-p operation component) (return))
 8.10614-      ;; Record the dependency. This hook is needed by POIU, which tracks a full dependency graph,
 8.10615-      ;; instead of just a dependency order as in vanilla ASDF.
 8.10616-      ;; TODO: It is also needed to detect OPERATE-in-PERFORM.
 8.10617-      (record-dependency plan operation component)
 8.10618-      (while-visiting-action (operation component) ; maintain context, handle circularity.
 8.10619-        ;; needed-in-image distinguishes b/w things that must happen in the
 8.10620-        ;; current image and those things that simply need to have been done in a previous one.
 8.10621-        (let* ((aniip (needed-in-image-p operation component)) ; action-specific needed-in-image
 8.10622-               ;; effective niip: meaningful for the action and required by the plan as traversed
 8.10623-               (eniip (and aniip needed-in-image-p))
 8.10624-               ;; status: have we traversed that action previously, and if so what was its status?
 8.10625-               (status (action-status plan operation component))
 8.10626-               (level (operate-level)))
 8.10627-          (when (and status
 8.10628-                     (or (status-done-p status) ;; all done
 8.10629-                         (and (status-need-p status) (<= level (status-level status))) ;; already visited
 8.10630-                         (and (status-keep-p status) (not eniip)))) ;; up-to-date and not eniip
 8.10631-            (return)) ; Already visited with sufficient need-in-image level!
 8.10632-          (labels ((visit-action (niip) ; We may visit the action twice, once with niip NIL, then T
 8.10633-                     (map-direct-dependencies ; recursively traverse dependencies
 8.10634-                      operation component #'(lambda (o c) (traverse-action plan o c niip)))
 8.10635-                     ;; AFTER dependencies have been traversed, compute action stamp
 8.10636-                     (let* ((status (if status
 8.10637-                                        (mark-status-needed status level)
 8.10638-                                        (compute-action-status plan operation component t)))
 8.10639-                            (out-of-date-p (not (status-keep-p status)))
 8.10640-                            (to-perform-p (or out-of-date-p (and niip (not (status-done-p status))))))
 8.10641-                       (cond ; it needs be done if it's out of date or needed in image but absent
 8.10642-                         ((and out-of-date-p (not niip)) ; if we need to do it,
 8.10643-                          (visit-action t)) ; then we need to do it *in the (current) image*!
 8.10644-                         (t
 8.10645-                          (setf (action-status plan operation component) status)
 8.10646-                          (when (status-done-p status)
 8.10647-                            (setf (component-operation-time operation component)
 8.10648-                                  (status-stamp status)))
 8.10649-                          (when to-perform-p ; if it needs to be added to the plan, count it
 8.10650-                            (incf (planned-action-count *asdf-session*))
 8.10651-                            (unless aniip ; if it's output-producing, count it
 8.10652-                              (incf (planned-output-action-count *asdf-session*)))))))))
 8.10653-            (visit-action eniip)))))) ; visit the action
 8.10654-
 8.10655-  ;; NB: This is not an error, not a warning, but a normal expected condition,
 8.10656-  ;; to be to signaled by FIND-SYSTEM when it detects an out-of-date system,
 8.10657-  ;; *before* it tries to replace it with a new definition.
 8.10658-  (define-condition system-out-of-date (condition)
 8.10659-    ((name :initarg :name :reader component-name))
 8.10660-    (:documentation "condition signaled when a system is detected as being out of date")
 8.10661-    (:report (lambda (c s)
 8.10662-               (format s "system ~A is out of date" (component-name c)))))
 8.10663-
 8.10664-  (defun action-up-to-date-p (plan operation component)
 8.10665-    "Check whether an action was up-to-date at the beginning of the session.
 8.10666-Update the VISITED-ACTIONS table with the known status, but don't add anything to the PLAN."
 8.10667-    (block nil
 8.10668-      (unless (action-valid-p operation component) (return t))
 8.10669-      (while-visiting-action (operation component) ; maintain context, handle circularity.
 8.10670-        ;; Do NOT record the dependency: it might be out of date.
 8.10671-        (let ((status (or (action-status plan operation component)
 8.10672-                          (setf (action-status plan operation component)
 8.10673-                                (let ((dependencies-up-to-date-p
 8.10674-                                       (handler-case
 8.10675-                                           (block nil
 8.10676-                                             (map-direct-dependencies
 8.10677-                                              operation component
 8.10678-                                              #'(lambda (o c)
 8.10679-                                                  (unless (action-up-to-date-p plan o c)
 8.10680-                                                    (return nil))))
 8.10681-                                             t)
 8.10682-                                         (system-out-of-date () nil))))
 8.10683-                                  (if dependencies-up-to-date-p
 8.10684-                                      (compute-action-status plan operation component nil)
 8.10685-                                      +status-void+))))))
 8.10686-          (and (status-keep-p status) (status-stamp status)))))))
 8.10687-
 8.10688-
 8.10689-;;;; Incidental traversals
 8.10690-
 8.10691-;;; Making a FILTERED-SEQUENTIAL-PLAN can be used to, e.g., all of the source
 8.10692-;;; files required by a bundling operation.
 8.10693-(with-upgradability ()
 8.10694-  (defclass filtered-sequential-plan (sequential-plan)
 8.10695-    ((component-type :initform t :initarg :component-type :reader plan-component-type)
 8.10696-     (keep-operation :initform t :initarg :keep-operation :reader plan-keep-operation)
 8.10697-     (keep-component :initform t :initarg :keep-component :reader plan-keep-component))
 8.10698-    (:documentation "A variant of SEQUENTIAL-PLAN that only records a subset of actions."))
 8.10699-
 8.10700-  (defmethod initialize-instance :after ((plan filtered-sequential-plan)
 8.10701-                                         &key system other-systems)
 8.10702-    ;; Ignore force and force-not, rely on other-systems:
 8.10703-    ;; force traversal of what we're interested in, i.e. current system or also others;
 8.10704-    ;; force-not traversal of what we're not interested in, i.e. other systems unless other-systems.
 8.10705-    (setf (slot-value plan 'forcing)
 8.10706-          (make-forcing :system system :force :all :force-not (if other-systems nil t))))
 8.10707-
 8.10708-  (defmethod plan-actions ((plan filtered-sequential-plan))
 8.10709-    (with-slots (keep-operation keep-component) plan
 8.10710-      (loop :for action :in (call-next-method)
 8.10711-        :as o = (action-operation action)
 8.10712-        :as c = (action-component action)
 8.10713-        :when (and (typep o keep-operation) (typep c keep-component))
 8.10714-        :collect (make-action o c))))
 8.10715-
 8.10716-  (defun collect-action-dependencies (plan operation component)
 8.10717-    (when (action-valid-p operation component)
 8.10718-      (while-visiting-action (operation component) ; maintain context, handle circularity.
 8.10719-        (let ((action (make-action operation component)))
 8.10720-          (unless (nth-value 1 (gethash action (visited-actions *asdf-session*)))
 8.10721-            (setf (gethash action (visited-actions *asdf-session*)) nil)
 8.10722-            (when (and (typep component (plan-component-type plan))
 8.10723-                       (not (action-forced-not-p (forcing plan) operation component)))
 8.10724-              (map-direct-dependencies operation component
 8.10725-                                       #'(lambda (o c) (collect-action-dependencies plan o c)))
 8.10726-              (push action (plan-actions-r plan))))))))
 8.10727-
 8.10728-  (defgeneric collect-dependencies (operation component &key &allow-other-keys)
 8.10729-    (:documentation "Given an action, build a plan for all of its dependencies."))
 8.10730-  (define-convenience-action-methods collect-dependencies (operation component &key))
 8.10731-  (defmethod collect-dependencies ((operation operation) (component component)
 8.10732-                                   &rest keys &key &allow-other-keys)
 8.10733-    (let ((plan (apply 'make-instance 'filtered-sequential-plan
 8.10734-                       :system (component-system component) keys)))
 8.10735-      (loop :for action :in (direct-dependencies operation component)
 8.10736-        :do (collect-action-dependencies plan (action-operation action) (action-component action)))
 8.10737-      (plan-actions plan)))
 8.10738-
 8.10739-  (defun required-components (system &rest keys &key (goal-operation 'load-op) &allow-other-keys)
 8.10740-    "Given a SYSTEM and a GOAL-OPERATION (default LOAD-OP), traverse the dependencies and
 8.10741-return a list of the components involved in building the desired action."
 8.10742-    (with-asdf-session (:override t)
 8.10743-      (remove-duplicates
 8.10744-       (mapcar 'action-component
 8.10745-               (apply 'collect-dependencies goal-operation system
 8.10746-                      (remove-plist-key :goal-operation keys)))
 8.10747-       :from-end t))))
 8.10748-
 8.10749-
 8.10750-;;;; High-level interface: make-plan, perform-plan
 8.10751-(with-upgradability ()
 8.10752-  (defgeneric make-plan (plan-class operation component &key &allow-other-keys)
 8.10753-    (:documentation "Generate and return a plan for performing OPERATION on COMPONENT."))
 8.10754-  (define-convenience-action-methods make-plan (plan-class operation component &key))
 8.10755-
 8.10756-  (defgeneric mark-as-done (plan-class operation component)
 8.10757-    (:documentation "Mark an action as done in a plan, after performing it."))
 8.10758-  (define-convenience-action-methods mark-as-done (plan-class operation component))
 8.10759-
 8.10760-  (defgeneric perform-plan (plan &key)
 8.10761-    (:documentation "Actually perform a plan and build the requested actions"))
 8.10762-
 8.10763-  (defparameter* *plan-class* 'sequential-plan
 8.10764-    "The default plan class to use when building with ASDF")
 8.10765-
 8.10766-  (defmethod make-plan (plan-class (o operation) (c component) &rest keys &key &allow-other-keys)
 8.10767-    (with-asdf-session ()
 8.10768-      (let ((plan (apply 'make-instance (or plan-class *plan-class*) keys)))
 8.10769-        (traverse-action plan o c t)
 8.10770-        plan)))
 8.10771-
 8.10772-  (defmethod perform-plan :around ((plan t) &key)
 8.10773-    (assert (performable-p (forcing plan)) () "plan not performable")
 8.10774-    (let ((*package* *package*)
 8.10775-          (*readtable* *readtable*))
 8.10776-      (with-compilation-unit () ;; backward-compatibility.
 8.10777-        (call-next-method))))   ;; Going forward, see deferred-warning support in lisp-build.
 8.10778-
 8.10779-  (defun action-already-done-p (plan operation component)
 8.10780-    (if-let (status (action-status plan operation component))
 8.10781-      (status-done-p status)))
 8.10782-
 8.10783-  (defmethod perform-plan ((plan t) &key)
 8.10784-    (loop :for action :in (plan-actions plan)
 8.10785-      :as o = (action-operation action)
 8.10786-      :as c = (action-component action) :do
 8.10787-      (unless (action-already-done-p plan o c)
 8.10788-        (perform-with-restarts o c)
 8.10789-        (mark-as-done plan o c))))
 8.10790-
 8.10791-  (defmethod mark-as-done ((plan plan) (o operation) (c component))
 8.10792-    (let ((plan-status (action-status plan o c))
 8.10793-          (perform-status (action-status nil o c)))
 8.10794-      (assert (and (status-stamp perform-status) (status-keep-p perform-status)) ()
 8.10795-              "Just performed ~A but failed to mark it done" (action-description o c))
 8.10796-      (setf (action-status plan o c)
 8.10797-            (make-action-status
 8.10798-             :bits (logior (status-bits plan-status) +done-bit+)
 8.10799-             :stamp (status-stamp perform-status)
 8.10800-             :level (status-level plan-status)
 8.10801-             :index (status-index plan-status))))))
 8.10802-;;;; -------------------------------------------------------------------------
 8.10803-;;;; Invoking Operations
 8.10804-
 8.10805-(uiop/package:define-package :asdf/operate
 8.10806-  (:recycle :asdf/operate :asdf)
 8.10807-  (:use :uiop/common-lisp :uiop :asdf/upgrade :asdf/session
 8.10808-        :asdf/component :asdf/system :asdf/system-registry :asdf/find-component
 8.10809-        :asdf/operation :asdf/action :asdf/lisp-action :asdf/forcing :asdf/plan)
 8.10810-  (:export
 8.10811-   #:operate #:oos #:build-op #:make
 8.10812-   #:load-system #:load-systems #:load-systems*
 8.10813-   #:compile-system #:test-system #:require-system #:module-provide-asdf
 8.10814-   #:component-loaded-p #:already-loaded-systems
 8.10815-   #:recursive-operate))
 8.10816-(in-package :asdf/operate)
 8.10817-
 8.10818-(with-upgradability ()
 8.10819-  (defgeneric operate (operation component &key)
 8.10820-    (:documentation
 8.10821-     "Operate does mainly four things for the user:
 8.10822-
 8.10823-1. Resolves the OPERATION designator into an operation object.
 8.10824-   OPERATION is typically a symbol denoting an operation class, instantiated with MAKE-OPERATION.
 8.10825-2. Resolves the COMPONENT designator into a component object.
 8.10826-   COMPONENT is typically a string or symbol naming a system, loaded from disk using FIND-SYSTEM.
 8.10827-3. It then calls MAKE-PLAN with the operation and system as arguments.
 8.10828-4. Finally calls PERFORM-PLAN on the resulting plan to actually build the system.
 8.10829-
 8.10830-The entire computation is wrapped in WITH-COMPILATION-UNIT and error handling code.
 8.10831-If a VERSION argument is supplied, then operate also ensures that the system found satisfies it
 8.10832-using the VERSION-SATISFIES method.
 8.10833-If a PLAN-CLASS argument is supplied, that class is used for the plan.
 8.10834-If a PLAN-OPTIONS argument is supplied, the options are passed to the plan.
 8.10835-
 8.10836-The :FORCE or :FORCE-NOT argument to OPERATE can be:
 8.10837-  T to force the inside of the specified system to be rebuilt (resp. not),
 8.10838-    without recursively forcing the other systems we depend on.
 8.10839-  :ALL to force all systems including other systems we depend on to be rebuilt (resp. not).
 8.10840-  (SYSTEM1 SYSTEM2 ... SYSTEMN) to force systems named in a given list
 8.10841-:FORCE-NOT has precedence over :FORCE; builtin systems cannot be forced.
 8.10842-
 8.10843-For backward compatibility, all keyword arguments are passed to MAKE-OPERATION
 8.10844-when instantiating a new operation, that will in turn be inherited by new operations.
 8.10845-But do NOT depend on it, for this is deprecated behavior."))
 8.10846-
 8.10847-  (define-convenience-action-methods operate (operation component &key)
 8.10848-    :if-no-component (error 'missing-component :requires component))
 8.10849-
 8.10850-  ;; This method ensures that an ASDF upgrade is attempted as the very first thing,
 8.10851-  ;; with suitable state preservation in case in case it actually happens,
 8.10852-  ;; and that a few suitable dynamic bindings are established.
 8.10853-  (defmethod operate :around (operation component &rest keys
 8.10854-                              &key verbose
 8.10855-                                (on-warnings *compile-file-warnings-behaviour*)
 8.10856-                                (on-failure *compile-file-failure-behaviour*))
 8.10857-    (nest
 8.10858-     (with-asdf-session ())
 8.10859-     (let* ((operation-remaker ;; how to remake the operation after ASDF was upgraded (if it was)
 8.10860-             (etypecase operation
 8.10861-               (operation (let ((name (type-of operation)))
 8.10862-                            #'(lambda () (make-operation name))))
 8.10863-               ((or symbol string) (constantly operation))))
 8.10864-            (component-path (typecase component ;; to remake the component after ASDF upgrade
 8.10865-                              (component (component-find-path component))
 8.10866-                              (t component)))
 8.10867-            (system-name (labels ((first-name (x)
 8.10868-                                    (etypecase x
 8.10869-                                      ((or string symbol) x) ; NB: includes the NIL case.
 8.10870-                                      (cons (or (first-name (car x)) (first-name (cdr x)))))))
 8.10871-                           (coerce-name (first-name component-path)))))
 8.10872-       (apply 'make-forcing :performable-p t :system system-name keys)
 8.10873-       ;; Before we operate on any system, make sure ASDF is up-to-date,
 8.10874-       ;; for if an upgrade is ever attempted at any later time, there may be BIG trouble.
 8.10875-       (unless (asdf-upgraded-p (toplevel-asdf-session))
 8.10876-         (setf (asdf-upgraded-p (toplevel-asdf-session)) t)
 8.10877-         (when (upgrade-asdf)
 8.10878-           ;; If we were upgraded, restart OPERATE the hardest of ways, for
 8.10879-           ;; its function may have been redefined.
 8.10880-           (return-from operate
 8.10881-             (with-asdf-session (:override t :override-cache t)
 8.10882-               (apply 'operate (funcall operation-remaker) component-path keys))))))
 8.10883-      ;; Setup proper bindings around any operate call.
 8.10884-     (let* ((*verbose-out* (and verbose *standard-output*))
 8.10885-            (*compile-file-warnings-behaviour* on-warnings)
 8.10886-            (*compile-file-failure-behaviour* on-failure)))
 8.10887-     (unwind-protect
 8.10888-          (progn
 8.10889-            (incf (operate-level))
 8.10890-            (call-next-method))
 8.10891-       (decf (operate-level)))))
 8.10892-
 8.10893-  (defmethod operate :before ((operation operation) (component component)
 8.10894-                              &key version)
 8.10895-    (unless (version-satisfies component version)
 8.10896-      (error 'missing-component-of-version :requires component :version version))
 8.10897-    (record-dependency nil operation component))
 8.10898-
 8.10899-  (defmethod operate ((operation operation) (component component)
 8.10900-                      &key plan-class plan-options)
 8.10901-    (let ((plan (apply 'make-plan plan-class operation component
 8.10902-                       :forcing (forcing *asdf-session*) plan-options)))
 8.10903-      (perform-plan plan)
 8.10904-      (values operation plan)))
 8.10905-
 8.10906-  (defun oos (operation component &rest args &key &allow-other-keys)
 8.10907-    (apply 'operate operation component args))
 8.10908-
 8.10909-  (setf (documentation 'oos 'function)
 8.10910-        (format nil "Short for _operate on system_ and an alias for the OPERATE function.~%~%~a"
 8.10911-                (documentation 'operate 'function)))
 8.10912-
 8.10913-  (define-condition recursive-operate (warning)
 8.10914-    ((operation :initarg :operation :reader condition-operation)
 8.10915-     (component :initarg :component :reader condition-component)
 8.10916-     (action :initarg :action :reader condition-action))
 8.10917-    (:report (lambda (c s)
 8.10918-               (format s (compatfmt "~@<Deprecated recursive use of (~S '~S '~S) while visiting ~S ~
 8.10919-- please use proper dependencies instead~@:>")
 8.10920-                       'operate
 8.10921-                       (type-of (condition-operation c))
 8.10922-                       (component-find-path (condition-component c))
 8.10923-                       (action-path (condition-action c)))))))
 8.10924-
 8.10925-;;;; Common operations
 8.10926-(when-upgrading ()
 8.10927-  (defmethod component-depends-on ((o prepare-op) (s system))
 8.10928-    (call-next-method)))
 8.10929-(with-upgradability ()
 8.10930-  (defclass build-op (non-propagating-operation) ()
 8.10931-    (:documentation "Since ASDF3, BUILD-OP is the recommended 'master' operation,
 8.10932-to operate by default on a system or component, via the function BUILD.
 8.10933-Its meaning is configurable via the :BUILD-OPERATION option of a component.
 8.10934-which typically specifies the name of a specific operation to which to delegate the build,
 8.10935-as a symbol or as a string later read as a symbol (after loading the defsystem-depends-on);
 8.10936-if NIL is specified (the default), BUILD-OP falls back to LOAD-OP,
 8.10937-that will load the system in the current image."))
 8.10938-  (defmethod component-depends-on ((o build-op) (c component))
 8.10939-    `((,(or (component-build-operation c) 'load-op) ,c)
 8.10940-      ,@(call-next-method)))
 8.10941-
 8.10942-  (defun make (system &rest keys)
 8.10943-    "The recommended way to interact with ASDF3.1 is via (ASDF:MAKE :FOO).
 8.10944-It will build system FOO using the operation BUILD-OP,
 8.10945-the meaning of which is configurable by the system, and
 8.10946-defaults to LOAD-OP, to load it in current image."
 8.10947-    (apply 'operate 'build-op system keys)
 8.10948-    t)
 8.10949-
 8.10950-  (defun load-system (system &rest keys &key force force-not verbose version &allow-other-keys)
 8.10951-    "Shorthand for `(operate 'asdf:load-op system)`. See OPERATE for details."
 8.10952-    (declare (ignore force force-not verbose version))
 8.10953-    (apply 'operate 'load-op system keys)
 8.10954-    t)
 8.10955-
 8.10956-  (defun load-systems* (systems &rest keys)
 8.10957-    "Loading multiple systems at once."
 8.10958-    (dolist (s systems) (apply 'load-system s keys)))
 8.10959-
 8.10960-  (defun load-systems (&rest systems)
 8.10961-    "Loading multiple systems at once."
 8.10962-    (load-systems* systems))
 8.10963-
 8.10964-  (defun compile-system (system &rest args &key force force-not verbose version &allow-other-keys)
 8.10965-    "Shorthand for `(asdf:operate 'asdf:compile-op system)`. See OPERATE for details."
 8.10966-    (declare (ignore force force-not verbose version))
 8.10967-    (apply 'operate 'compile-op system args)
 8.10968-    t)
 8.10969-
 8.10970-  (defun test-system (system &rest args &key force force-not verbose version &allow-other-keys)
 8.10971-    "Shorthand for `(asdf:operate 'asdf:test-op system)`. See OPERATE for details."
 8.10972-    (declare (ignore force force-not verbose version))
 8.10973-    (apply 'operate 'test-op system args)
 8.10974-    t))
 8.10975-
 8.10976-;;;;; Define the function REQUIRE-SYSTEM, that, similarly to REQUIRE,
 8.10977-;; only tries to load its specified target if it's not loaded yet.
 8.10978-(with-upgradability ()
 8.10979-  (defun component-loaded-p (component)
 8.10980-    "Has the given COMPONENT been successfully loaded in the current image (yet)?
 8.10981-Note that this returns true even if the component is not up to date."
 8.10982-    (if-let ((component (find-component component () :registered t)))
 8.10983-      (nth-value 1 (component-operation-time (make-operation 'load-op) component))))
 8.10984-
 8.10985-  (defun already-loaded-systems ()
 8.10986-    "return a list of the names of the systems that have been successfully loaded so far"
 8.10987-    (mapcar 'coerce-name (remove-if-not 'component-loaded-p (registered-systems*)))))
 8.10988-
 8.10989-
 8.10990-;;;; Define the class REQUIRE-SYSTEM, to be hooked into CL:REQUIRE when possible,
 8.10991-;; i.e. for ABCL, CLISP, ClozureCL, CMUCL, ECL, MKCL and SBCL
 8.10992-;; Note that despite the two being homonyms, the _function_ require-system
 8.10993-;; and the _class_ require-system are quite distinct entities, fulfilling independent purposes.
 8.10994-(with-upgradability ()
 8.10995-  (defvar *modules-being-required* nil)
 8.10996-
 8.10997-  (defclass require-system (system)
 8.10998-    ((module :initarg :module :initform nil :accessor required-module))
 8.10999-    (:documentation "A SYSTEM subclass whose processing is handled by
 8.11000-the implementation's REQUIRE rather than by internal ASDF mechanisms."))
 8.11001-
 8.11002-  (defmethod perform ((o compile-op) (c require-system))
 8.11003-    nil)
 8.11004-
 8.11005-  (defmethod perform ((o load-op) (s require-system))
 8.11006-    (let* ((module (or (required-module s) (coerce-name s)))
 8.11007-           (*modules-being-required* (cons module *modules-being-required*)))
 8.11008-      (assert (null (component-children s)))
 8.11009-      (require module)))
 8.11010-
 8.11011-  (defmethod resolve-dependency-combination (component (combinator (eql :require)) arguments)
 8.11012-    (unless (and (length=n-p arguments 1)
 8.11013-                 (typep (car arguments) '(or string (and symbol (not null)))))
 8.11014-      (parameter-error (compatfmt "~@<In ~S, bad dependency ~S for ~S. ~S takes one argument, a string or non-null symbol~@:>")
 8.11015-                       'resolve-dependency-combination
 8.11016-                       (cons combinator arguments) component combinator))
 8.11017-    ;; :require must be prepared for some implementations providing modules using ASDF,
 8.11018-    ;; as SBCL used to do, and others may might do. Thus, the system provided in the end
 8.11019-    ;; would be a downcased name as per module-provide-asdf above. For the same reason,
 8.11020-    ;; we cannot assume that the system in the end will be of type require-system,
 8.11021-    ;; but must check whether we can use find-system and short-circuit cl:require.
 8.11022-    ;; Otherwise, calling cl:require could result in nasty reentrant calls between
 8.11023-    ;; cl:require and asdf:operate that could potentially blow up the stack,
 8.11024-    ;; all the while defeating the consistency of the dependency graph.
 8.11025-    (let* ((module (car arguments)) ;; NB: we already checked that it was not null
 8.11026-           ;; CMUCL, MKCL, SBCL like their module names to be all upcase.
 8.11027-           (module-name (string module))
 8.11028-           (system-name (string-downcase module))
 8.11029-           (system (find-system system-name nil)))
 8.11030-      (or system (let ((system (make-instance 'require-system :name system-name :module module-name)))
 8.11031-                   (register-system system)
 8.11032-                   system))))
 8.11033-
 8.11034-  (defun module-provide-asdf (name)
 8.11035-    ;; We must use string-downcase, because modules are traditionally specified as symbols,
 8.11036-    ;; that implementations traditionally normalize as uppercase, for which we seek a system
 8.11037-    ;; with a name that is traditionally in lowercase. Case is lost along the way. That's fine.
 8.11038-    ;; We could make complex, non-portable rules to try to preserve case, and just documenting
 8.11039-    ;; them would be a hell that it would be a disservice to inflict on users.
 8.11040-    (let ((module-name (string name))
 8.11041-          (system-name (string-downcase name)))
 8.11042-      (unless (member module-name *modules-being-required* :test 'equal)
 8.11043-        (let ((*modules-being-required* (cons module-name *modules-being-required*))
 8.11044-              #+sbcl (sb-impl::*requiring* (remove module-name sb-impl::*requiring* :test 'equal)))
 8.11045-          (handler-bind
 8.11046-              (((or style-warning recursive-operate) #'muffle-warning)
 8.11047-               (missing-component (constantly nil))
 8.11048-               (fatal-condition
 8.11049-                #'(lambda (e)
 8.11050-                    (format *error-output* (compatfmt "~@<ASDF could not load ~(~A~) because ~A.~@:>~%")
 8.11051-                            name e))))
 8.11052-            (let ((*verbose-out* (make-broadcast-stream)))
 8.11053-              (let ((system (find-system system-name nil)))
 8.11054-                (when system
 8.11055-                  ;; Do not use require-system after all, use load-system:
 8.11056-                  ;; on the one hand, REQUIRE already uses *MODULES* not to load something twice,
 8.11057-                  ;; on the other hand, REQUIRE-SYSTEM uses FORCE-NOT which may conflict with
 8.11058-                  ;; the toplevel session forcing settings.
 8.11059-                  (load-system system :verbose nil)
 8.11060-                  t)))))))))
 8.11061-
 8.11062-
 8.11063-;;;; Some upgrade magic
 8.11064-(with-upgradability ()
 8.11065-  (defun restart-upgraded-asdf ()
 8.11066-    ;; If we're in the middle of something, restart it.
 8.11067-    (let ((systems-being-defined
 8.11068-           (when *asdf-session*
 8.11069-             (prog1
 8.11070-                 (loop :for k :being :the hash-keys :of (asdf-cache)
 8.11071-                   :when (eq (first k) 'find-system) :collect (second k))
 8.11072-               (clrhash (asdf-cache))))))
 8.11073-      ;; Regardless, clear defined systems, since they might be invalid
 8.11074-      ;; after an incompatible ASDF upgrade.
 8.11075-      (clear-registered-systems)
 8.11076-      ;; The configuration also may have to be upgraded.
 8.11077-      (upgrade-configuration)
 8.11078-      ;; If we were in the middle of an operation, be sure to restore the system being defined.
 8.11079-      (dolist (s systems-being-defined) (find-system s nil))))
 8.11080-  (register-hook-function '*post-upgrade-cleanup-hook* 'restart-upgraded-asdf))
 8.11081-;;;; -------------------------------------------------------------------------
 8.11082-;;;; Finding systems
 8.11083-
 8.11084-(uiop/package:define-package :asdf/find-system
 8.11085-  (:recycle :asdf/find-system :asdf)
 8.11086-  (:use :uiop/common-lisp :uiop :asdf/upgrade
 8.11087-        :asdf/session :asdf/component :asdf/system :asdf/operation :asdf/action :asdf/lisp-action
 8.11088-        :asdf/find-component :asdf/system-registry :asdf/plan :asdf/operate)
 8.11089-  (:import-from #:asdf/component #:%additional-input-files)
 8.11090-  (:export
 8.11091-   #:find-system #:locate-system #:load-asd #:define-op
 8.11092-   #:load-system-definition-error #:error-name #:error-pathname #:error-condition))
 8.11093-(in-package :asdf/find-system)
 8.11094-
 8.11095-(with-upgradability ()
 8.11096-  (define-condition load-system-definition-error (system-definition-error)
 8.11097-    ((name :initarg :name :reader error-name)
 8.11098-     (pathname :initarg :pathname :reader error-pathname)
 8.11099-     (condition :initarg :condition :reader error-condition))
 8.11100-    (:report (lambda (c s)
 8.11101-               (format s (compatfmt "~@<Error while trying to load definition for system ~A from pathname ~A: ~3i~_~A~@:>")
 8.11102-                       (error-name c) (error-pathname c) (error-condition c)))))
 8.11103-
 8.11104-
 8.11105-  ;;; Methods for find-system
 8.11106-
 8.11107-  ;; Reject NIL as a system designator.
 8.11108-  (defmethod find-system ((name null) &optional (error-p t))
 8.11109-    (when error-p
 8.11110-      (sysdef-error (compatfmt "~@<NIL is not a valid system name~@:>"))))
 8.11111-
 8.11112-  ;; Default method for find-system: resolve the argument using COERCE-NAME.
 8.11113-  (defmethod find-system (name &optional (error-p t))
 8.11114-    (find-system (coerce-name name) error-p))
 8.11115-
 8.11116-  (defun find-system-if-being-defined (name)
 8.11117-    ;; This function finds systems being defined *in the current ASDF session*, as embodied by
 8.11118-    ;; its session cache, even before they are fully defined and registered in *registered-systems*.
 8.11119-    ;; The purpose of this function is to prevent races between two files that might otherwise
 8.11120-    ;; try overwrite each other's system objects, resulting in infinite loops and stack overflow.
 8.11121-    ;; This function explicitly MUST NOT find definitions merely registered in previous sessions.
 8.11122-    ;; NB: this function depends on a corresponding side-effect in parse-defsystem;
 8.11123-    ;; the precise protocol between the two functions may change in the future (or not).
 8.11124-    (first (gethash `(find-system ,(coerce-name name)) (asdf-cache))))
 8.11125-
 8.11126-  (defclass define-op (non-propagating-operation) ()
 8.11127-    (:documentation "An operation to record dependencies on loading a .asd file."))
 8.11128-
 8.11129-  (defmethod record-dependency ((plan null) (operation t) (component t))
 8.11130-    (unless (or (typep operation 'define-op)
 8.11131-                (and (typep operation 'load-op)
 8.11132-                     (typep component 'system)
 8.11133-                     (equal "asdf" (coerce-name component))))
 8.11134-      (if-let ((action (first (visiting-action-list *asdf-session*))))
 8.11135-        (let ((parent-operation (action-operation action))
 8.11136-              (parent-component (action-component action)))
 8.11137-          (cond
 8.11138-            ((and (typep parent-operation 'define-op)
 8.11139-                  (typep parent-component 'system))
 8.11140-             (let ((action (cons operation component)))
 8.11141-               (unless (gethash action (definition-dependency-set parent-component))
 8.11142-                 (push (cons operation component) (definition-dependency-list parent-component))
 8.11143-                 (setf (gethash action (definition-dependency-set parent-component)) t))))
 8.11144-            (t
 8.11145-             (warn 'recursive-operate
 8.11146-                   :operation operation :component component :action action)))))))
 8.11147-
 8.11148-  (defmethod component-depends-on ((o define-op) (s system))
 8.11149-    `(;;NB: 1- ,@(system-defsystem-depends-on s)) ; Should be already included in the below.
 8.11150-      ;; 2- We don't call-next-method to avoid other methods
 8.11151-      ,@(loop :for (o . c) :in (definition-dependency-list s) :collect (list o c))))
 8.11152-
 8.11153-  (defmethod component-depends-on ((o operation) (s system))
 8.11154-    `(,@(when (and (not (typep o 'define-op))
 8.11155-                   (or (system-source-file s) (definition-dependency-list s)))
 8.11156-              `((define-op ,(primary-system-name s))))
 8.11157-      ,@(call-next-method)))
 8.11158-
 8.11159-  (defmethod perform ((o operation) (c undefined-system))
 8.11160-    (sysdef-error "Trying to use undefined or incompletely defined system ~A" (coerce-name c)))
 8.11161-
 8.11162-  ;; TODO: could this file be refactored so that locate-system is merely
 8.11163-  ;; the cache-priming call to input-files here?
 8.11164-  (defmethod input-files ((o define-op) (s system))
 8.11165-    (if-let ((asd (system-source-file s))) (list asd)))
 8.11166-
 8.11167-  (defmethod perform ((o define-op) (s system))
 8.11168-    (nest
 8.11169-     (if-let ((pathname (first (input-files o s)))))
 8.11170-     (let ((readtable *readtable*) ;; save outer syntax tables. TODO: proper syntax-control
 8.11171-           (print-pprint-dispatch *print-pprint-dispatch*)))
 8.11172-     (with-standard-io-syntax)
 8.11173-     (let ((*print-readably* nil)
 8.11174-           ;; Note that our backward-compatible *readtable* is
 8.11175-           ;; a global readtable that gets globally side-effected. Ouch.
 8.11176-           ;; Same for the *print-pprint-dispatch* table.
 8.11177-           ;; We should do something about that for ASDF3 if possible, or else ASDF4.
 8.11178-           (*readtable* readtable) ;; restore inside syntax table
 8.11179-           (*print-pprint-dispatch* print-pprint-dispatch)
 8.11180-           (*package* (find-package :asdf-user))
 8.11181-           (*default-pathname-defaults*
 8.11182-            ;; resolve logical-pathnames so they won't wreak havoc in parsing namestrings.
 8.11183-            (pathname-directory-pathname (physicalize-pathname pathname)))))
 8.11184-     (handler-bind
 8.11185-         (((and error (not missing-component))
 8.11186-           #'(lambda (condition)
 8.11187-               (error 'load-system-definition-error
 8.11188-                      :name (coerce-name s) :pathname pathname :condition condition))))
 8.11189-       (asdf-message (compatfmt "~&~@<; ~@;Loading system definition~@[ for ~A~] from ~A~@:>~%")
 8.11190-                     (coerce-name s) pathname)
 8.11191-       ;; dependencies will depend on what's loaded via definition-dependency-list
 8.11192-       (unset-asdf-cache-entry `(component-depends-on ,o ,s))
 8.11193-       (unset-asdf-cache-entry `(input-files ,o ,s)))
 8.11194-     (load* pathname :external-format (encoding-external-format (detect-encoding pathname)))))
 8.11195-
 8.11196-  (defun load-asd (pathname &key name)
 8.11197-    "Load system definitions from PATHNAME.
 8.11198-NAME if supplied is the name of a system expected to be defined in that file.
 8.11199-
 8.11200-Do NOT try to load a .asd file directly with CL:LOAD. Always use ASDF:LOAD-ASD."
 8.11201-    (with-asdf-session ()
 8.11202-      ;; TODO: use OPERATE, so we consult the cache and only load once per session.
 8.11203-      (flet ((do-it (o c) (operate o c)))
 8.11204-        (let ((primary-name (primary-system-name (or name (pathname-name pathname))))
 8.11205-              (operation (make-operation 'define-op)))
 8.11206-          (if-let (system (registered-system primary-name))
 8.11207-            (progn
 8.11208-              ;; We already determine this to be obsolete ---
 8.11209-              ;; or should we move some tests from find-system to check for up-to-date-ness here?
 8.11210-              (setf (component-operation-time operation system) t
 8.11211-                    (definition-dependency-list system) nil
 8.11212-                    (definition-dependency-set system) (list-to-hash-set nil))
 8.11213-              (do-it operation system))
 8.11214-            (let ((system (make-instance 'undefined-system
 8.11215-                                         :name primary-name :source-file pathname)))
 8.11216-              (register-system system)
 8.11217-              (unwind-protect (do-it operation system)
 8.11218-                (when (typep system 'undefined-system)
 8.11219-                  (clear-system system)))))))))
 8.11220-
 8.11221-  (defvar *old-asdf-systems* (make-hash-table :test 'equal))
 8.11222-
 8.11223-  ;; (Private) function to check that a system that was found isn't an asdf downgrade.
 8.11224-  ;; Returns T if everything went right, NIL if the system was an ASDF at an older version,
 8.11225-  ;; or UIOP of the same or older version, that shall not be loaded.
 8.11226-  ;; Also issue a warning if it was a strictly older version of ASDF.
 8.11227-  (defun check-not-old-asdf-system (name pathname)
 8.11228-    (or (not (member name '("asdf" "uiop") :test 'equal))
 8.11229-        (null pathname)
 8.11230-        (let* ((asdfp (equal name "asdf")) ;; otherwise, it's uiop
 8.11231-               (version-pathname
 8.11232-                (subpathname pathname "version" :type (if asdfp "lisp-expr" "lisp")))
 8.11233-               (version (and (probe-file* version-pathname :truename nil)
 8.11234-                             (read-file-form version-pathname :at (if asdfp '(0) '(2 2 2)))))
 8.11235-               (old-version (asdf-version)))
 8.11236-          (cond
 8.11237-            ;; Same version is OK for ASDF, to allow loading from modified source.
 8.11238-            ;; However, do *not* load UIOP of the exact same version:
 8.11239-            ;; it was already loaded it as part of ASDF and would only be double-loading.
 8.11240-            ;; Be quiet about it, though, since it's a normal situation.
 8.11241-            ((equal old-version version) asdfp)
 8.11242-            ((version< old-version version) t) ;; newer version: Good!
 8.11243-            (t ;; old version: bad
 8.11244-             (ensure-gethash
 8.11245-              (list (namestring pathname) version) *old-asdf-systems*
 8.11246-              #'(lambda ()
 8.11247-                  (let ((old-pathname (system-source-file (registered-system "asdf"))))
 8.11248-                    (if asdfp
 8.11249-                        (warn "~@<~
 8.11250-        You are using ASDF version ~A ~:[(probably from (require \"asdf\") ~
 8.11251-        or loaded by quicklisp)~;from ~:*~S~] and have an older version of ASDF ~
 8.11252-        ~:[(and older than 2.27 at that)~;~:*~A~] registered at ~S. ~
 8.11253-        Having an ASDF installed and registered is the normal way of configuring ASDF to upgrade itself, ~
 8.11254-        and having an old version registered is a configuration error. ~
 8.11255-        ASDF will ignore this configured system rather than downgrade itself. ~
 8.11256-        In the future, you may want to either: ~
 8.11257-        (a) upgrade this configured ASDF to a newer version, ~
 8.11258-        (b) install a newer ASDF and register it in front of the former in your configuration, or ~
 8.11259-        (c) uninstall or unregister this and any other old version of ASDF from your configuration. ~
 8.11260-        Note that the older ASDF might be registered implicitly through configuration inherited ~
 8.11261-        from your system installation, in which case you might have to specify ~
 8.11262-        :ignore-inherited-configuration in your in your ~~/.config/common-lisp/source-registry.conf ~
 8.11263-        or other source-registry configuration file, environment variable or lisp parameter. ~
 8.11264-        Indeed, a likely offender is an obsolete version of the cl-asdf debian or ubuntu package, ~
 8.11265-        that you might want to upgrade (if a recent enough version is available) ~
 8.11266-        or else remove altogether (since most implementations ship with a recent asdf); ~
 8.11267-        if you lack the system administration rights to upgrade or remove this package, ~
 8.11268-        then you might indeed want to either install and register a more recent version, ~
 8.11269-        or use :ignore-inherited-configuration to avoid registering the old one. ~
 8.11270-        Please consult ASDF documentation and/or experts.~@:>~%"
 8.11271-                              old-version old-pathname version pathname)
 8.11272-                        ;; NB: for UIOP, don't warn, just ignore.
 8.11273-                        (warn "ASDF ~A (from ~A), UIOP ~A (from ~A)"
 8.11274-                              old-version old-pathname version pathname)
 8.11275-                        ))))
 8.11276-             nil))))) ;; only issue the warning the first time, but always return nil
 8.11277-
 8.11278-  (defun locate-system (name)
 8.11279-    "Given a system NAME designator, try to locate where to load the system from.
 8.11280-Returns six values: FOUNDP FOUND-SYSTEM PATHNAME PREVIOUS PREVIOUS-TIME PREVIOUS-PRIMARY
 8.11281-FOUNDP is true when a system was found,
 8.11282-either a new unregistered one or a previously registered one.
 8.11283-FOUND-SYSTEM when not null is a SYSTEM object that may be REGISTER-SYSTEM'ed.
 8.11284-PATHNAME when not null is a path from which to load the system,
 8.11285-either associated with FOUND-SYSTEM, or with the PREVIOUS system.
 8.11286-PREVIOUS when not null is a previously loaded SYSTEM object of same name.
 8.11287-PREVIOUS-TIME when not null is the time at which the PREVIOUS system was loaded.
 8.11288-PREVIOUS-PRIMARY when not null is the primary system for the PREVIOUS system."
 8.11289-    (with-asdf-session () ;; NB: We don't cache the results. We once used to, but it wasn't useful,
 8.11290-      ;; and keeping a negative cache was a bug (see lp#1335323), which required
 8.11291-      ;; explicit invalidation in clear-system and find-system (when unsucccessful).
 8.11292-      (let* ((name (coerce-name name))
 8.11293-             (previous (registered-system name)) ; load from disk if absent or newer on disk
 8.11294-             (previous-primary-name (and previous (primary-system-name previous)))
 8.11295-             (previous-primary-system (and previous-primary-name
 8.11296-                                           (registered-system previous-primary-name)))
 8.11297-             (previous-time (and previous-primary-system
 8.11298-                                 (component-operation-time 'define-op previous-primary-system)))
 8.11299-             (found (search-for-system-definition name))
 8.11300-             (found-system (and (typep found 'system) found))
 8.11301-             (pathname (ensure-pathname
 8.11302-                        (or (and (typep found '(or pathname string)) (pathname found))
 8.11303-                            (system-source-file found-system)
 8.11304-                            (system-source-file previous))
 8.11305-                        :want-absolute t :resolve-symlinks *resolve-symlinks*))
 8.11306-             (foundp (and (or found-system pathname previous) t)))
 8.11307-        (check-type found (or null pathname system))
 8.11308-        (unless (check-not-old-asdf-system name pathname)
 8.11309-          (check-type previous system) ;; asdf is preloaded, so there should be a previous one.
 8.11310-          (setf found-system nil pathname nil))
 8.11311-        (values foundp found-system pathname previous previous-time previous-primary-system))))
 8.11312-
 8.11313-  ;; TODO: make a prepare-define-op node for this
 8.11314-  ;; so we can properly cache the answer rather than recompute it.
 8.11315-  (defun definition-dependencies-up-to-date-p (system)
 8.11316-    (check-type system system)
 8.11317-    (or (not (primary-system-p system))
 8.11318-        (handler-case
 8.11319-            (loop :with plan = (make-instance *plan-class*)
 8.11320-              :for action :in (definition-dependency-list system)
 8.11321-              :always (action-up-to-date-p
 8.11322-                       plan (action-operation action) (action-component action))
 8.11323-              :finally
 8.11324-              (let ((o (make-operation 'define-op)))
 8.11325-                (multiple-value-bind (stamp done-p)
 8.11326-                    (compute-action-stamp plan o system)
 8.11327-                  (return (and (timestamp<= stamp (component-operation-time o system))
 8.11328-                               done-p)))))
 8.11329-          (system-out-of-date () nil))))
 8.11330-
 8.11331-  ;; Main method for find-system: first, make sure the computation is memoized in a session cache.
 8.11332-  ;; Unless the system is immutable, use locate-system to find the primary system;
 8.11333-  ;; reconcile the finding (if any) with any previous definition (in a previous session,
 8.11334-  ;; preloaded, with a previous configuration, or before filesystem changes), and
 8.11335-  ;; load a found .asd if appropriate. Finally, update registration table and return results.
 8.11336-  (defmethod find-system ((name string) &optional (error-p t))
 8.11337-    (nest
 8.11338-     (with-asdf-session (:key `(find-system ,name)))
 8.11339-     (let ((name-primary-p (primary-system-p name)))
 8.11340-       (unless name-primary-p (find-system (primary-system-name name) nil)))
 8.11341-     (or (and *immutable-systems* (gethash name *immutable-systems*) (registered-system name)))
 8.11342-     (multiple-value-bind (foundp found-system pathname previous previous-time previous-primary)
 8.11343-         (locate-system name)
 8.11344-       (assert (eq foundp (and (or found-system pathname previous) t))))
 8.11345-     (let ((previous-pathname (system-source-file previous))
 8.11346-           (system (or previous found-system)))
 8.11347-       (when (and found-system (not previous))
 8.11348-         (register-system found-system))
 8.11349-       (when (and system pathname)
 8.11350-         (setf (system-source-file system) pathname))
 8.11351-       (if-let ((stamp (get-file-stamp pathname)))
 8.11352-         (let ((up-to-date-p
 8.11353-                (and previous previous-primary
 8.11354-                     (or (pathname-equal pathname previous-pathname)
 8.11355-                         (and pathname previous-pathname
 8.11356-                              (pathname-equal
 8.11357-                               (physicalize-pathname pathname)
 8.11358-                               (physicalize-pathname previous-pathname))))
 8.11359-                     (timestamp<= stamp previous-time)
 8.11360-                     ;; Check that all previous definition-dependencies are up-to-date,
 8.11361-                     ;; traversing them without triggering the adding of nodes to the plan.
 8.11362-                     ;; TODO: actually have a prepare-define-op, extract its timestamp,
 8.11363-                     ;; and check that it is less than the stamp of the previous define-op ?
 8.11364-                     (definition-dependencies-up-to-date-p previous-primary))))
 8.11365-           (unless up-to-date-p
 8.11366-             (restart-case
 8.11367-                 (signal 'system-out-of-date :name name)
 8.11368-               (continue () :report "continue"))
 8.11369-             (load-asd pathname :name name)))))
 8.11370-     ;; Try again after having loaded from disk if needed
 8.11371-     (or (registered-system name)
 8.11372-         (when error-p (error 'missing-component :requires name)))))
 8.11373-
 8.11374-  ;; Resolved forward reference for asdf/system-registry.
 8.11375-  (defun mark-component-preloaded (component)
 8.11376-    "Mark a component as preloaded."
 8.11377-    (let ((component (find-component component nil :registered t)))
 8.11378-      ;; Recurse to children, so asdf/plan will hopefully be happy.
 8.11379-      (map () 'mark-component-preloaded (component-children component))
 8.11380-      ;; Mark the timestamps of the common lisp-action operations as 0.
 8.11381-      (let ((cot (component-operation-times component)))
 8.11382-        (dolist (o `(,@(when (primary-system-p component) '(define-op))
 8.11383-                       prepare-op compile-op load-op))
 8.11384-          (setf (gethash (make-operation o) cot) 0))))))
 8.11385-;;;; -------------------------------------------------------------------------
 8.11386-;;;; Defsystem
 8.11387-
 8.11388-(uiop/package:define-package :asdf/parse-defsystem
 8.11389-  (:recycle :asdf/parse-defsystem :asdf/defsystem :asdf)
 8.11390-  (:nicknames :asdf/defsystem) ;; previous name, to be compatible with, in case anyone cares
 8.11391-  (:use :uiop/common-lisp :asdf/driver :asdf/upgrade
 8.11392-   :asdf/session :asdf/component :asdf/system :asdf/system-registry
 8.11393-   :asdf/find-component :asdf/action :asdf/lisp-action :asdf/operate)
 8.11394-  (:import-from :asdf/system #:depends-on #:weakly-depends-on)
 8.11395-  ;; these needed for record-additional-system-input-file
 8.11396-  (:import-from :asdf/operation #:make-operation)
 8.11397-  (:import-from :asdf/component #:%additional-input-files)
 8.11398-  (:import-from :asdf/find-system #:define-op)
 8.11399-  (:export
 8.11400-   #:defsystem #:register-system-definition
 8.11401-   #:*default-component-class*
 8.11402-   #:determine-system-directory #:parse-component-form
 8.11403-   #:non-toplevel-system #:non-system-system #:bad-system-name
 8.11404-   #:*known-systems-with-bad-secondary-system-names*
 8.11405-   #:known-system-with-bad-secondary-system-names-p
 8.11406-   #:sysdef-error-component #:check-component-input
 8.11407-   #:explain
 8.11408-   ;; for extending the component types
 8.11409-   #:compute-component-children
 8.11410-   #:class-for-type))
 8.11411-(in-package :asdf/parse-defsystem)
 8.11412-
 8.11413-;;; Pathname
 8.11414-(with-upgradability ()
 8.11415-  (defun determine-system-directory (pathname)
 8.11416-    ;; The defsystem macro calls this function to determine the pathname of a system as follows:
 8.11417-    ;; 1. If the pathname argument is an pathname object (NOT a namestring),
 8.11418-    ;;    that is already an absolute pathname, return it.
 8.11419-    ;; 2. Otherwise, the directory containing the LOAD-PATHNAME
 8.11420-    ;;    is considered (as deduced from e.g. *LOAD-PATHNAME*), and
 8.11421-    ;;    if it is indeed available and an absolute pathname, then
 8.11422-    ;;    the PATHNAME argument is normalized to a relative pathname
 8.11423-    ;;    as per PARSE-UNIX-NAMESTRING (with ENSURE-DIRECTORY T)
 8.11424-    ;;    and merged into that DIRECTORY as per SUBPATHNAME.
 8.11425-    ;;    Note: avoid *COMPILE-FILE-PATHNAME* because the .asd is loaded as source,
 8.11426-    ;;    but may be from within the EVAL-WHEN of a file compilation.
 8.11427-    ;; If no absolute pathname was found, we return NIL.
 8.11428-    (check-type pathname (or null string pathname))
 8.11429-    (pathname-directory-pathname
 8.11430-     (resolve-symlinks*
 8.11431-      (ensure-absolute-pathname
 8.11432-       (parse-unix-namestring pathname :type :directory)
 8.11433-       #'(lambda () (ensure-absolute-pathname
 8.11434-                     (load-pathname) 'get-pathname-defaults nil))
 8.11435-       nil)))))
 8.11436-
 8.11437-
 8.11438-(when-upgrading (:version "3.3.4.17")
 8.11439-  ;; This turned into a generic function in 3.3.4.17
 8.11440-  (fmakunbound 'class-for-type))
 8.11441-
 8.11442-;;; Component class
 8.11443-(with-upgradability ()
 8.11444-  ;; What :file gets interpreted as, unless overridden by a :default-component-class
 8.11445-  (defvar *default-component-class* 'cl-source-file)
 8.11446-
 8.11447-  (defgeneric class-for-type (parent type-designator)
 8.11448-    (:documentation
 8.11449-     "Return a CLASS object to be used to instantiate components specified by TYPE-DESIGNATOR in the context of PARENT."))
 8.11450-
 8.11451-  (defmethod class-for-type ((parent null) type)
 8.11452-    "If the PARENT is NIL, then TYPE must designate a subclass of SYSTEM."
 8.11453-    (or (coerce-class type :package :asdf/interface :super 'system :error nil)
 8.11454-        (sysdef-error "don't recognize component type ~S in the context of no parent" type)))
 8.11455-
 8.11456-  (defmethod class-for-type ((parent parent-component) type)
 8.11457-    (or (coerce-class type :package :asdf/interface :super 'component :error nil)
 8.11458-        (and (eq type :file)
 8.11459-             (coerce-class
 8.11460-              (or (loop :for p = parent :then (component-parent p) :while p
 8.11461-                          :thereis (module-default-component-class p))
 8.11462-                  *default-component-class*)
 8.11463-              :package :asdf/interface :super 'component :error nil))
 8.11464-        (sysdef-error "don't recognize component type ~S" type))))
 8.11465-
 8.11466-
 8.11467-;;; Check inputs
 8.11468-(with-upgradability ()
 8.11469-  (define-condition non-system-system (system-definition-error)
 8.11470-    ((name :initarg :name :reader non-system-system-name)
 8.11471-     (class-name :initarg :class-name :reader non-system-system-class-name))
 8.11472-    (:report (lambda (c s)
 8.11473-               (format s (compatfmt "~@<Error while defining system ~S: class ~S isn't a subclass of ~S~@:>")
 8.11474-                       (non-system-system-name c) (non-system-system-class-name c) 'system))))
 8.11475-
 8.11476-  (define-condition non-toplevel-system (system-definition-error)
 8.11477-    ((parent :initarg :parent :reader non-toplevel-system-parent)
 8.11478-     (name :initarg :name :reader non-toplevel-system-name))
 8.11479-    (:report (lambda (c s)
 8.11480-               (format s (compatfmt "~@<Error while defining system: component ~S claims to have a system ~S as a child~@:>")
 8.11481-                       (non-toplevel-system-parent c) (non-toplevel-system-name c)))))
 8.11482-
 8.11483-  (define-condition bad-system-name (warning)
 8.11484-    ((name :initarg :name :reader component-name)
 8.11485-     (source-file :initarg :source-file :reader system-source-file))
 8.11486-    (:report (lambda (c s)
 8.11487-               (let* ((file (system-source-file c))
 8.11488-                      (name (component-name c))
 8.11489-                      (asd (pathname-name file)))
 8.11490-                 (format s (compatfmt "~@<System definition file ~S contains definition for system ~S. ~
 8.11491-Please only define ~S and secondary systems with a name starting with ~S (e.g. ~S) in that file.~@:>")
 8.11492-                       file name asd (strcat asd "/") (strcat asd "/test"))))))
 8.11493-
 8.11494-  (defun sysdef-error-component (msg type name value)
 8.11495-    (sysdef-error (strcat msg (compatfmt "~&~@<The value specified for ~(~A~) ~A is ~S~@:>"))
 8.11496-                  type name value))
 8.11497-
 8.11498-  (defun check-component-input (type name weakly-depends-on
 8.11499-                                depends-on components)
 8.11500-    "A partial test of the values of a component."
 8.11501-    (unless (listp depends-on)
 8.11502-      (sysdef-error-component ":depends-on must be a list."
 8.11503-                              type name depends-on))
 8.11504-    (unless (listp weakly-depends-on)
 8.11505-      (sysdef-error-component ":weakly-depends-on must be a list."
 8.11506-                              type name weakly-depends-on))
 8.11507-    (unless (listp components)
 8.11508-      (sysdef-error-component ":components must be NIL or a list of components."
 8.11509-                              type name components)))
 8.11510-
 8.11511-
 8.11512-  (defun record-additional-system-input-file (pathname component parent)
 8.11513-    (let* ((record-on (if parent
 8.11514-                          (loop :with retval
 8.11515-                                :for par = parent :then (component-parent par)
 8.11516-                                :while par
 8.11517-                                :do (setf retval par)
 8.11518-                                :finally (return retval))
 8.11519-                          component))
 8.11520-           (comp (if (typep record-on 'component)
 8.11521-                     record-on
 8.11522-                     ;; at this point there will be no parent for RECORD-ON
 8.11523-                     (find-component record-on nil)))
 8.11524-           (op (make-operation 'define-op))
 8.11525-           (cell (or (assoc op (%additional-input-files comp))
 8.11526-                       (let ((new-cell (list op)))
 8.11527-                         (push new-cell (%additional-input-files comp))
 8.11528-                         new-cell))))
 8.11529-      (pushnew pathname (cdr cell) :test 'pathname-equal)
 8.11530-      (values)))
 8.11531-
 8.11532-  ;; Given a form used as :version specification, in the context of a system definition
 8.11533-  ;; in a file at PATHNAME, for given COMPONENT with given PARENT, normalize the form
 8.11534-  ;; to an acceptable ASDF-format version.
 8.11535-  (fmakunbound 'normalize-version) ;; signature changed between 2.27 and 2.31
 8.11536-  (defun normalize-version (form &key pathname component parent)
 8.11537-    (labels ((invalid (&optional (continuation "using NIL instead"))
 8.11538-               (warn (compatfmt "~@<Invalid :version specifier ~S~@[ for component ~S~]~@[ in ~S~]~@[ from file ~S~]~@[, ~A~]~@:>")
 8.11539-                     form component parent pathname continuation))
 8.11540-             (invalid-parse (control &rest args)
 8.11541-               (unless (if-let (target (find-component parent component)) (builtin-system-p target))
 8.11542-                 (apply 'warn control args)
 8.11543-                 (invalid))))
 8.11544-      (if-let (v (typecase form
 8.11545-                   ((or string null) form)
 8.11546-                   (real
 8.11547-                    (invalid "Substituting a string")
 8.11548-                    (format nil "~D" form)) ;; 1.0 becomes "1.0"
 8.11549-                   (cons
 8.11550-                    (case (first form)
 8.11551-                      ((:read-file-form)
 8.11552-                       (destructuring-bind (subpath &key (at 0)) (rest form)
 8.11553-                         (let ((path (subpathname pathname subpath)))
 8.11554-                           (record-additional-system-input-file path component parent)
 8.11555-                           (safe-read-file-form path
 8.11556-                                                :at at :package :asdf-user))))
 8.11557-                      ((:read-file-line)
 8.11558-                       (destructuring-bind (subpath &key (at 0)) (rest form)
 8.11559-                         (let ((path (subpathname pathname subpath)))
 8.11560-                           (record-additional-system-input-file path component parent)
 8.11561-                           (safe-read-file-line (subpathname pathname subpath)
 8.11562-                                                :at at))))
 8.11563-                      (otherwise
 8.11564-                       (invalid))))
 8.11565-                   (t
 8.11566-                    (invalid))))
 8.11567-        (if-let (pv (parse-version v #'invalid-parse))
 8.11568-          (unparse-version pv)
 8.11569-          (invalid))))))
 8.11570-
 8.11571-
 8.11572-;;; "inline methods"
 8.11573-(with-upgradability ()
 8.11574-  (defparameter* +asdf-methods+
 8.11575-      '(perform-with-restarts perform explain output-files operation-done-p))
 8.11576-
 8.11577-  (defun %remove-component-inline-methods (component)
 8.11578-    (dolist (name +asdf-methods+)
 8.11579-      (map ()
 8.11580-           ;; this is inefficient as most of the stored
 8.11581-           ;; methods will not be for this particular gf
 8.11582-           ;; But this is hardly performance-critical
 8.11583-           #'(lambda (m)
 8.11584-               (remove-method (symbol-function name) m))
 8.11585-           (component-inline-methods component)))
 8.11586-    (component-inline-methods component) nil)
 8.11587-
 8.11588-  (defparameter *standard-method-combination-qualifiers*
 8.11589-    '(:around :before :after))
 8.11590-
 8.11591-;;; Find inline method definitions of the form
 8.11592-;;;
 8.11593-;;;   :perform (test-op :before (operation component) ...)
 8.11594-;;;
 8.11595-;;; in REST (which is the plist of all DEFSYSTEM initargs) and define the specified methods.
 8.11596-  (defun %define-component-inline-methods (ret rest)
 8.11597-    ;; find key-value pairs that look like inline method definitions in REST. For each identified
 8.11598-    ;; definition, parse it and, if it is well-formed, define the method.
 8.11599-    (loop :for (key value) :on rest :by #'cddr
 8.11600-          :for name = (and (keywordp key) (find key +asdf-methods+ :test 'string=))
 8.11601-          :when name :do
 8.11602-            ;; parse VALUE as an inline method definition of the form
 8.11603-            ;;
 8.11604-            ;;   (OPERATION-NAME [QUALIFIER] (OPERATION-PARAMETER COMPONENT-PARAMETER) &rest BODY)
 8.11605-            (destructuring-bind (operation-name &rest rest) value
 8.11606-              (let ((qualifiers '()))
 8.11607-                ;; ensure that OPERATION-NAME is a symbol.
 8.11608-                (unless (and (symbolp operation-name) (not (null operation-name)))
 8.11609-                  (sysdef-error "Ill-formed inline method: ~S. The first element is not a symbol ~
 8.11610-                              designating an operation but ~S."
 8.11611-                                value operation-name))
 8.11612-                ;; ensure that REST starts with either a cons (potential lambda list, further checked
 8.11613-                ;; below) or a qualifier accepted by the standard method combination. Everything else
 8.11614-                ;; is ill-formed. In case of a valid qualifier, pop it from REST so REST now definitely
 8.11615-                ;; has to start with the lambda list.
 8.11616-                (cond
 8.11617-                  ((consp (car rest)))
 8.11618-                  ((not (member (car rest)
 8.11619-                                *standard-method-combination-qualifiers*))
 8.11620-                   (sysdef-error "Ill-formed inline method: ~S. Only a single of the standard ~
 8.11621-                               qualifiers ~{~S~^ ~} is allowed, not ~S."
 8.11622-                                 value *standard-method-combination-qualifiers* (car rest)))
 8.11623-                  (t
 8.11624-                   (setf qualifiers (list (pop rest)))))
 8.11625-                ;; REST must start with a two-element lambda list.
 8.11626-                (unless (and (listp (car rest))
 8.11627-                             (length=n-p (car rest) 2)
 8.11628-                             (null (cddar rest)))
 8.11629-                  (sysdef-error "Ill-formed inline method: ~S. The operation name ~S is not followed by ~
 8.11630-                              a lambda-list of the form (OPERATION COMPONENT) and a method body."
 8.11631-                                value operation-name))
 8.11632-                ;; define the method.
 8.11633-                (destructuring-bind ((o c) &rest body) rest
 8.11634-                  (pushnew
 8.11635-                   (eval `(defmethod ,name ,@qualifiers ((,o ,operation-name) (,c (eql ,ret))) ,@body))
 8.11636-                   (component-inline-methods ret)))))))
 8.11637-
 8.11638-  (defun %refresh-component-inline-methods (component rest)
 8.11639-    ;; clear methods, then add the new ones
 8.11640-    (%remove-component-inline-methods component)
 8.11641-    (%define-component-inline-methods component rest)))
 8.11642-
 8.11643-
 8.11644-;;; Main parsing function
 8.11645-(with-upgradability ()
 8.11646-  (defun parse-dependency-def (dd)
 8.11647-    (if (listp dd)
 8.11648-        (case (first dd)
 8.11649-          (:feature
 8.11650-           (unless (= (length dd) 3)
 8.11651-             (sysdef-error "Ill-formed feature dependency: ~s" dd))
 8.11652-           (let ((embedded (parse-dependency-def (third dd))))
 8.11653-             `(:feature ,(second dd) ,embedded)))
 8.11654-          (feature
 8.11655-           (sysdef-error "`feature' has been removed from the dependency spec language of ASDF. Use :feature instead in ~s." dd))
 8.11656-          (:require
 8.11657-           (unless (= (length dd) 2)
 8.11658-             (sysdef-error "Ill-formed require dependency: ~s" dd))
 8.11659-           dd)
 8.11660-          (:version
 8.11661-           (unless (= (length dd) 3)
 8.11662-             (sysdef-error "Ill-formed version dependency: ~s" dd))
 8.11663-           `(:version ,(coerce-name (second dd)) ,(third dd)))
 8.11664-          (otherwise (sysdef-error "Ill-formed dependency: ~s" dd)))
 8.11665-      (coerce-name dd)))
 8.11666-
 8.11667-  (defun parse-dependency-defs (dd-list)
 8.11668-    "Parse the dependency defs in DD-LIST into canonical form by translating all
 8.11669-system names contained using COERCE-NAME. Return the result."
 8.11670-    (mapcar 'parse-dependency-def dd-list))
 8.11671-
 8.11672-  (defgeneric compute-component-children (component components serial-p)
 8.11673-    (:documentation
 8.11674-     "Return a list of children for COMPONENT.
 8.11675-
 8.11676-COMPONENTS is a list of the explicitly defined children descriptions.
 8.11677-
 8.11678-SERIAL-P is non-NIL if each child in COMPONENTS should depend on the previous
 8.11679-children."))
 8.11680-
 8.11681-  (defun stable-union (s1 s2 &key (test #'eql) (key 'identity))
 8.11682-   (append s1
 8.11683-     (remove-if #'(lambda (e2) (member (funcall key e2) (funcall key s1) :test test)) s2)))
 8.11684-
 8.11685-  (defun parse-component-form (parent options &key previous-serial-components)
 8.11686-    (destructuring-bind
 8.11687-        (type name &rest rest &key
 8.11688-                                (builtin-system-p () bspp)
 8.11689-                                ;; the following list of keywords is reproduced below in the
 8.11690-                                ;; remove-plist-keys form.  important to keep them in sync
 8.11691-                                components pathname perform explain output-files operation-done-p
 8.11692-                                weakly-depends-on depends-on serial
 8.11693-                                do-first if-component-dep-fails version
 8.11694-                                ;; list ends
 8.11695-         &allow-other-keys) options
 8.11696-      (declare (ignore perform explain output-files operation-done-p builtin-system-p))
 8.11697-      (check-component-input type name weakly-depends-on depends-on components)
 8.11698-      (when (and parent
 8.11699-                 (find-component parent name)
 8.11700-                 (not ;; ignore the same object when rereading the defsystem
 8.11701-                  (typep (find-component parent name)
 8.11702-                         (class-for-type parent type))))
 8.11703-        (error 'duplicate-names :name name))
 8.11704-      (when do-first (error "DO-FIRST is not supported anymore as of ASDF 3"))
 8.11705-      (let* ((name (coerce-name name))
 8.11706-             (args `(:name ,name
 8.11707-                     :pathname ,pathname
 8.11708-                     ,@(when parent `(:parent ,parent))
 8.11709-                     ,@(remove-plist-keys
 8.11710-                        '(:components :pathname :if-component-dep-fails :version
 8.11711-                          :perform :explain :output-files :operation-done-p
 8.11712-                          :weakly-depends-on :depends-on :serial)
 8.11713-                        rest)))
 8.11714-             (component (find-component parent name))
 8.11715-             (class (class-for-type parent type)))
 8.11716-        (when (and parent (subtypep class 'system))
 8.11717-          (error 'non-toplevel-system :parent parent :name name))
 8.11718-        (if component ; preserve identity
 8.11719-            (apply 'reinitialize-instance component args)
 8.11720-            (setf component (apply 'make-instance class args)))
 8.11721-        (component-pathname component) ; eagerly compute the absolute pathname
 8.11722-        (when (typep component 'system)
 8.11723-          ;; cache information for introspection
 8.11724-          (setf (slot-value component 'depends-on)
 8.11725-                (parse-dependency-defs depends-on)
 8.11726-                (slot-value component 'weakly-depends-on)
 8.11727-                ;; these must be a list of systems, cannot be features or versioned systems
 8.11728-                (mapcar 'coerce-name weakly-depends-on)))
 8.11729-        (let ((sysfile (system-source-file (component-system component)))) ;; requires the previous
 8.11730-          (when (and (typep component 'system) (not bspp))
 8.11731-            (setf (builtin-system-p component) (lisp-implementation-pathname-p sysfile)))
 8.11732-          (setf version (normalize-version version :component name :parent parent :pathname sysfile)))
 8.11733-        ;; Don't use the accessor: kluge to avoid upgrade issue on CCL 1.8.
 8.11734-        ;; A better fix is required.
 8.11735-        (setf (slot-value component 'version) version)
 8.11736-        (when (typep component 'parent-component)
 8.11737-          (setf (component-children component) (compute-component-children component components serial))
 8.11738-          (compute-children-by-name component))
 8.11739-        (when previous-serial-components
 8.11740-          (setf depends-on (stable-union depends-on previous-serial-components :test #'equal)))
 8.11741-        (when weakly-depends-on
 8.11742-          ;; ASDF4: deprecate this feature and remove it.
 8.11743-          (appendf depends-on
 8.11744-                   (remove-if (complement #'(lambda (x) (find-system x nil))) weakly-depends-on)))
 8.11745-        ;; Used by POIU. ASDF4: rename to component-depends-on?
 8.11746-        (setf (component-sideway-dependencies component) depends-on)
 8.11747-        (%refresh-component-inline-methods component rest)
 8.11748-        (when if-component-dep-fails
 8.11749-          (error "The system definition for ~S uses deprecated ~
 8.11750-            ASDF option :IF-COMPONENT-DEP-FAILS. ~
 8.11751-            Starting with ASDF 3, please use :IF-FEATURE instead"
 8.11752-                 (coerce-name (component-system component))))
 8.11753-        component)))
 8.11754-
 8.11755-  (defmethod compute-component-children ((component parent-component) components serial-p)
 8.11756-    (loop
 8.11757-      :with previous-components = nil ; list of strings
 8.11758-      :for c-form :in components
 8.11759-      :for c = (parse-component-form component c-form
 8.11760-                                     :previous-serial-components previous-components)
 8.11761-      :for name :of-type string = (component-name c)
 8.11762-      :when serial-p
 8.11763-        ;; if this is an if-feature component, we need to make a serial link
 8.11764-        ;; from previous components to following components -- otherwise should
 8.11765-        ;; the IF-FEATURE component drop out, the chain of serial dependencies will be
 8.11766-        ;; broken.
 8.11767-        :unless (component-if-feature c)
 8.11768-          :do (setf previous-components nil)
 8.11769-        :end
 8.11770-        :and
 8.11771-          :do (push name previous-components)
 8.11772-      :end
 8.11773-      :collect c))
 8.11774-
 8.11775-  ;; the following are all systems that Stas Boukarev maintains and refuses to fix,
 8.11776-  ;; hoping instead to make my life miserable. Instead, I just make ASDF ignore them.
 8.11777-  (defparameter* *known-systems-with-bad-secondary-system-names*
 8.11778-    (list-to-hash-set '("cl-ppcre" "cl-interpol")))
 8.11779-  (defun known-system-with-bad-secondary-system-names-p (asd-name)
 8.11780-    ;; Does .asd file with name ASD-NAME contain known exceptions
 8.11781-    ;; that should be screened out of checking for BAD-SYSTEM-NAME?
 8.11782-    (gethash asd-name *known-systems-with-bad-secondary-system-names*))
 8.11783-
 8.11784-  (defun register-system-definition
 8.11785-      (name &rest options &key pathname (class 'system) (source-file () sfp)
 8.11786-                            defsystem-depends-on &allow-other-keys)
 8.11787-    ;; The system must be registered before we parse the body,
 8.11788-    ;; otherwise we recur when trying to find an existing system
 8.11789-    ;; of the same name to reuse options (e.g. pathname) from.
 8.11790-    ;; To avoid infinite recursion in cases where you defsystem a system
 8.11791-    ;; that is registered to a different location to find-system,
 8.11792-    ;; we also need to remember it in the asdf-cache.
 8.11793-    (nest
 8.11794-     (with-asdf-session ())
 8.11795-     (let* ((name (coerce-name name))
 8.11796-            (source-file (if sfp source-file (resolve-symlinks* (load-pathname))))))
 8.11797-     (flet ((fix-case (x) (if (logical-pathname-p source-file) (string-downcase x) x))))
 8.11798-     (let* ((asd-name (and source-file
 8.11799-                           (equal "asd" (fix-case (pathname-type source-file)))
 8.11800-                           (fix-case (pathname-name source-file))))
 8.11801-            ;; note that PRIMARY-NAME is a *syntactically* primary name
 8.11802-            (primary-name (primary-system-name name)))
 8.11803-       (when (and asd-name
 8.11804-                  (not (equal asd-name primary-name))
 8.11805-                  (not (known-system-with-bad-secondary-system-names-p asd-name)))
 8.11806-         (warn (make-condition 'bad-system-name :source-file source-file :name name))))
 8.11807-     (let* (;; NB: handle defsystem-depends-on BEFORE to create the system object,
 8.11808-            ;; so that in case it fails, there is no incomplete object polluting the build.
 8.11809-            (checked-defsystem-depends-on
 8.11810-             (let* ((dep-forms (parse-dependency-defs defsystem-depends-on))
 8.11811-                    (deps (loop :for spec :in dep-forms
 8.11812-                            :when (resolve-dependency-spec nil spec)
 8.11813-                            :collect :it)))
 8.11814-               (load-systems* deps)
 8.11815-               dep-forms))
 8.11816-            (system (or (find-system-if-being-defined name)
 8.11817-                        (if-let (registered (registered-system name))
 8.11818-                          (reset-system-class registered 'undefined-system
 8.11819-                                              :name name :source-file source-file)
 8.11820-                          (register-system (make-instance 'undefined-system
 8.11821-                                                          :name name :source-file source-file)))))
 8.11822-            (component-options
 8.11823-             (append
 8.11824-              (remove-plist-keys '(:defsystem-depends-on :class) options)
 8.11825-              ;; cache defsystem-depends-on in canonical form
 8.11826-              (when checked-defsystem-depends-on
 8.11827-                `(:defsystem-depends-on ,checked-defsystem-depends-on))))
 8.11828-            (directory (determine-system-directory pathname)))
 8.11829-       ;; This works hand in hand with asdf/find-system:find-system-if-being-defined:
 8.11830-       (set-asdf-cache-entry `(find-system ,name) (list system)))
 8.11831-     ;; We change-class AFTER we loaded the defsystem-depends-on
 8.11832-     ;; since the class might be defined as part of those.
 8.11833-     (let ((class (class-for-type nil class)))
 8.11834-       (unless (subtypep class 'system)
 8.11835-         (error 'non-system-system :name name :class-name (class-name class)))
 8.11836-       (unless (eq (type-of system) class)
 8.11837-         (reset-system-class system class)))
 8.11838-     (parse-component-form nil (list* :system name :pathname directory component-options))))
 8.11839-
 8.11840-  (defmacro defsystem (name &body options)
 8.11841-    `(apply 'register-system-definition ',name ',options)))
 8.11842-;;;; -------------------------------------------------------------------------
 8.11843-;;;; ASDF-Bundle
 8.11844-
 8.11845-(uiop/package:define-package :asdf/bundle
 8.11846-  (:recycle :asdf/bundle :asdf)
 8.11847-  (:use :uiop/common-lisp :uiop :asdf/upgrade
 8.11848-   :asdf/component :asdf/system :asdf/operation
 8.11849-   :asdf/find-component ;; used by ECL
 8.11850-   :asdf/action :asdf/lisp-action :asdf/plan :asdf/operate :asdf/parse-defsystem)
 8.11851-  (:export
 8.11852-   #:bundle-op #:bundle-type #:program-system
 8.11853-   #:bundle-system #:bundle-pathname-type #:direct-dependency-files
 8.11854-   #:monolithic-op #:monolithic-bundle-op #:operation-monolithic-p
 8.11855-   #:basic-compile-bundle-op #:prepare-bundle-op
 8.11856-   #:compile-bundle-op #:load-bundle-op #:monolithic-compile-bundle-op #:monolithic-load-bundle-op
 8.11857-   #:lib-op #:monolithic-lib-op
 8.11858-   #:dll-op #:monolithic-dll-op
 8.11859-   #:deliver-asd-op #:monolithic-deliver-asd-op
 8.11860-   #:program-op #:image-op #:compiled-file #:precompiled-system #:prebuilt-system
 8.11861-   #:user-system-p #:user-system #:trivial-system-p
 8.11862-   #:prologue-code #:epilogue-code #:static-library))
 8.11863-(in-package :asdf/bundle)
 8.11864-
 8.11865-(with-upgradability ()
 8.11866-  (defclass bundle-op (operation) ()
 8.11867-    (:documentation "base class for operations that bundle outputs from multiple components"))
 8.11868-  (defgeneric bundle-type (bundle-op))
 8.11869-
 8.11870-  (defclass monolithic-op (operation) ()
 8.11871-    (:documentation "A MONOLITHIC operation operates on a system *and all of its
 8.11872-dependencies*.  So, for example, a monolithic concatenate operation will
 8.11873-concatenate together a system's components and all of its dependencies, but a
 8.11874-simple concatenate operation will concatenate only the components of the system
 8.11875-itself."))
 8.11876-
 8.11877-  (defclass monolithic-bundle-op (bundle-op monolithic-op)
 8.11878-    ;; Old style way of specifying prologue and epilogue on ECL: in the monolithic operation.
 8.11879-    ;; DEPRECATED. Supported replacement: Define slots on program-system instead.
 8.11880-    ((prologue-code :initform nil :accessor prologue-code)
 8.11881-     (epilogue-code :initform nil :accessor epilogue-code))
 8.11882-    (:documentation "operations that are both monolithic-op and bundle-op"))
 8.11883-
 8.11884-  (defclass program-system (system)
 8.11885-    ;; New style (ASDF3.1) way of specifying prologue and epilogue on ECL: in the system
 8.11886-    ((prologue-code :initform nil :initarg :prologue-code :reader prologue-code)
 8.11887-     (epilogue-code :initform nil :initarg :epilogue-code :reader epilogue-code)
 8.11888-     (no-uiop :initform nil :initarg :no-uiop :reader no-uiop)
 8.11889-     (prefix-lisp-object-files :initarg :prefix-lisp-object-files
 8.11890-                               :initform nil :accessor prefix-lisp-object-files)
 8.11891-     (postfix-lisp-object-files :initarg :postfix-lisp-object-files
 8.11892-                                :initform nil :accessor postfix-lisp-object-files)
 8.11893-     (extra-object-files :initarg :extra-object-files
 8.11894-                         :initform nil :accessor extra-object-files)
 8.11895-     (extra-build-args :initarg :extra-build-args
 8.11896-                       :initform nil :accessor extra-build-args)))
 8.11897-
 8.11898-  (defmethod prologue-code ((x system)) nil)
 8.11899-  (defmethod epilogue-code ((x system)) nil)
 8.11900-  (defmethod no-uiop ((x system)) nil)
 8.11901-  (defmethod prefix-lisp-object-files ((x system)) nil)
 8.11902-  (defmethod postfix-lisp-object-files ((x system)) nil)
 8.11903-  (defmethod extra-object-files ((x system)) nil)
 8.11904-  (defmethod extra-build-args ((x system)) nil)
 8.11905-
 8.11906-  (defclass link-op (bundle-op) ()
 8.11907-    (:documentation "Abstract operation for linking files together"))
 8.11908-
 8.11909-  (defclass gather-operation (bundle-op) ()
 8.11910-    (:documentation "Abstract operation for gathering many input files from a system"))
 8.11911-  (defgeneric gather-operation (gather-operation))
 8.11912-  (defmethod gather-operation ((o gather-operation)) nil)
 8.11913-  (defgeneric gather-type (gather-operation))
 8.11914-
 8.11915-  (defun operation-monolithic-p (op)
 8.11916-    (typep op 'monolithic-op))
 8.11917-
 8.11918-  ;; Dependencies of a gather-op are the actions of the dependent operation
 8.11919-  ;; for all the (sorted) required components for loading the system.
 8.11920-  ;; Monolithic operations typically use lib-op as the dependent operation,
 8.11921-  ;; and all system-level dependencies as required components.
 8.11922-  ;; Non-monolithic operations typically use compile-op as the dependent operation,
 8.11923-  ;; and all transitive sub-components as required components (excluding other systems).
 8.11924-  (defmethod component-depends-on ((o gather-operation) (s system))
 8.11925-    (let* ((mono (operation-monolithic-p o))
 8.11926-           (go (make-operation (or (gather-operation o) 'compile-op)))
 8.11927-           (bundle-p (typep go 'bundle-op))
 8.11928-           ;; In a non-mono operation, don't recurse to other systems.
 8.11929-           ;; In a mono operation gathering bundles, don't recurse inside systems.
 8.11930-           (component-type (if mono (if bundle-p 'system t) '(not system)))
 8.11931-           ;; In the end, only keep system bundles or non-system bundles, depending.
 8.11932-           (keep-component (if bundle-p 'system '(not system)))
 8.11933-           (deps
 8.11934-            ;; Required-components only looks at the dependencies of an action, excluding the action
 8.11935-            ;; itself, so it may be safely used by an action recursing on its dependencies (which
 8.11936-            ;; may or may not be an overdesigned API, since in practice we never use it that way).
 8.11937-            ;; Therefore, if we use :goal-operation 'load-op :keep-operation 'load-op, which looks
 8.11938-            ;; cleaner, we will miss the load-op on the requested system itself, which doesn't
 8.11939-            ;; matter for a regular system, but matters, a lot, for a package-inferred-system.
 8.11940-            ;; Using load-op as the goal operation and basic-compile-op as the keep-operation works
 8.11941-            ;; for our needs of gathering all the files we want to include in a bundle.
 8.11942-            ;; Note that we use basic-compile-op rather than compile-op so it will still work on
 8.11943-            ;; systems that would somehow load dependencies with load-bundle-op.
 8.11944-            (required-components
 8.11945-             s :other-systems mono :component-type component-type :keep-component keep-component
 8.11946-             :goal-operation 'load-op :keep-operation 'basic-compile-op)))
 8.11947-      `((,go ,@deps) ,@(call-next-method))))
 8.11948-
 8.11949-  ;; Create a single fasl for the entire library
 8.11950-  (defclass basic-compile-bundle-op (bundle-op basic-compile-op) ()
 8.11951-    (:documentation "Base class for compiling into a bundle"))
 8.11952-  (defmethod bundle-type ((o basic-compile-bundle-op)) :fasb)
 8.11953-  (defmethod gather-type ((o basic-compile-bundle-op))
 8.11954-    #-(or clasp ecl mkcl) :fasl
 8.11955-    #+(or clasp ecl mkcl) :object)
 8.11956-
 8.11957-  ;; Analog to prepare-op, for load-bundle-op and compile-bundle-op
 8.11958-  (defclass prepare-bundle-op (sideway-operation)
 8.11959-    ((sideway-operation
 8.11960-      :initform #+(or clasp ecl mkcl) 'load-bundle-op #-(or clasp ecl mkcl) 'load-op
 8.11961-      :allocation :class))
 8.11962-    (:documentation "Operation class for loading the bundles of a system's dependencies"))
 8.11963-
 8.11964-  (defclass lib-op (link-op gather-operation non-propagating-operation) ()
 8.11965-    (:documentation "Compile the system and produce a linkable static library (.a/.lib)
 8.11966-for all the linkable object files associated with the system. Compare with DLL-OP.
 8.11967-
 8.11968-On most implementations, these object files only include extensions to the runtime
 8.11969-written in C or another language with a compiler producing linkable object files.
 8.11970-On CLASP, ECL, MKCL, these object files _also_ include the contents of Lisp files
 8.11971-themselves. In any case, this operation will produce what you need to further build
 8.11972-a static runtime for your system, or a dynamic library to load in an existing runtime."))
 8.11973-  (defmethod bundle-type ((o lib-op)) :lib)
 8.11974-  (defmethod gather-type ((o lib-op)) :object)
 8.11975-
 8.11976-  ;; What works: on ECL, CLASP(?), MKCL, we link the many .o files from the system into the .so;
 8.11977-  ;; on other implementations, we combine (usually concatenate) the .fasl files into one.
 8.11978-  (defclass compile-bundle-op (basic-compile-bundle-op selfward-operation gather-operation
 8.11979-                                                       #+(or clasp ecl mkcl) link-op)
 8.11980-    ((selfward-operation :initform '(prepare-bundle-op) :allocation :class))
 8.11981-    (:documentation "This operator is an alternative to COMPILE-OP. Build a system
 8.11982-and all of its dependencies, but build only a single (\"monolithic\") FASL, instead
 8.11983-of one per source file, which may be more resource efficient.  That monolithic
 8.11984-FASL should be loaded with LOAD-BUNDLE-OP, rather than LOAD-OP."))
 8.11985-
 8.11986-  (defclass load-bundle-op (basic-load-op selfward-operation)
 8.11987-    ((selfward-operation :initform '(prepare-bundle-op compile-bundle-op) :allocation :class))
 8.11988-    (:documentation "This operator is an alternative to LOAD-OP. Build a system
 8.11989-and all of its dependencies, using COMPILE-BUNDLE-OP. The difference with
 8.11990-respect to LOAD-OP is that it builds only a single FASL, which may be
 8.11991-faster and more resource efficient."))
 8.11992-
 8.11993-  ;; NB: since the monolithic-op's can't be sideway-operation's,
 8.11994-  ;; if we wanted lib-op, dll-op, deliver-asd-op to be sideway-operation's,
 8.11995-  ;; we'd have to have the monolithic-op not inherit from the main op,
 8.11996-  ;; but instead inherit from a basic-FOO-op as with basic-compile-bundle-op above.
 8.11997-
 8.11998-  (defclass dll-op (link-op gather-operation non-propagating-operation) ()
 8.11999-    (:documentation "Compile the system and produce a dynamic loadable library (.so/.dll)
 8.12000-for all the linkable object files associated with the system. Compare with LIB-OP."))
 8.12001-  (defmethod bundle-type ((o dll-op)) :dll)
 8.12002-  (defmethod gather-type ((o dll-op)) :object)
 8.12003-
 8.12004-  (defclass deliver-asd-op (basic-compile-op selfward-operation)
 8.12005-    ((selfward-operation
 8.12006-      ;; TODO: implement link-op on all implementations, and make that
 8.12007-      ;; '(compile-bundle-op lib-op #-(or clasp ecl mkcl) dll-op)
 8.12008-      :initform '(compile-bundle-op #+(or clasp ecl mkcl) lib-op)
 8.12009-      :allocation :class))
 8.12010-    (:documentation "produce an asd file for delivering the system as a single fasl"))
 8.12011-
 8.12012-
 8.12013-  (defclass monolithic-deliver-asd-op (deliver-asd-op monolithic-bundle-op)
 8.12014-    ((selfward-operation
 8.12015-      ;; TODO: implement link-op on all implementations, and make that
 8.12016-      ;; '(monolithic-compile-bundle-op monolithic-lib-op #-(or clasp ecl mkcl) monolithic-dll-op)
 8.12017-      :initform '(monolithic-compile-bundle-op #+(or clasp ecl mkcl) monolithic-lib-op)
 8.12018-      :allocation :class))
 8.12019-    (:documentation "produce fasl and asd files for combined system and dependencies."))
 8.12020-
 8.12021-  (defclass monolithic-compile-bundle-op
 8.12022-      (basic-compile-bundle-op monolithic-bundle-op
 8.12023-       #+(or clasp ecl mkcl) link-op gather-operation non-propagating-operation)
 8.12024-    ()
 8.12025-    (:documentation "Create a single fasl for the system and its dependencies."))
 8.12026-
 8.12027-  (defclass monolithic-load-bundle-op (load-bundle-op monolithic-bundle-op)
 8.12028-    ((selfward-operation :initform 'monolithic-compile-bundle-op :allocation :class))
 8.12029-    (:documentation "Load a single fasl for the system and its dependencies."))
 8.12030-
 8.12031-  (defclass monolithic-lib-op (lib-op monolithic-bundle-op non-propagating-operation) ()
 8.12032-    (:documentation "Compile the system and produce a linkable static library (.a/.lib)
 8.12033-for all the linkable object files associated with the system or its dependencies. See LIB-OP."))
 8.12034-
 8.12035-  (defclass monolithic-dll-op (dll-op monolithic-bundle-op non-propagating-operation) ()
 8.12036-    (:documentation "Compile the system and produce a dynamic loadable library (.so/.dll)
 8.12037-for all the linkable object files associated with the system or its dependencies. See LIB-OP"))
 8.12038-
 8.12039-  (defclass image-op (monolithic-bundle-op selfward-operation
 8.12040-                      #+(or clasp ecl mkcl) link-op #+(or clasp ecl mkcl) gather-operation)
 8.12041-    ((selfward-operation :initform '(#-(or clasp ecl mkcl) load-op) :allocation :class))
 8.12042-    (:documentation "create an image file from the system and its dependencies"))
 8.12043-  (defmethod bundle-type ((o image-op)) :image)
 8.12044-  #+(or clasp ecl mkcl) (defmethod gather-operation ((o image-op)) 'lib-op)
 8.12045-  #+(or clasp ecl mkcl) (defmethod gather-type ((o image-op)) :static-library)
 8.12046-
 8.12047-  (defclass program-op (image-op) ()
 8.12048-    (:documentation "create an executable file from the system and its dependencies"))
 8.12049-  (defmethod bundle-type ((o program-op)) :program)
 8.12050-
 8.12051-  ;; From the ASDF-internal bundle-type identifier, get a filesystem-usable pathname type.
 8.12052-  (defun bundle-pathname-type (bundle-type)
 8.12053-    (etypecase bundle-type
 8.12054-      ((or null string) ;; pass through nil or string literal
 8.12055-       bundle-type)
 8.12056-      ((eql :no-output-file) ;; marker for a bundle-type that has NO output file
 8.12057-       (error "No output file, therefore no pathname type"))
 8.12058-      ((eql :fasl) ;; the type of a fasl
 8.12059-       (compile-file-type)) ; on image-based platforms, used as input and output
 8.12060-      ((eql :fasb) ;; the type of a fasl
 8.12061-       #-(or clasp ecl mkcl) (compile-file-type) ; on image-based platforms, used as input and output
 8.12062-       #+(or ecl mkcl) "fasb"
 8.12063-       #+clasp "fasp") ; on C-linking platforms, only used as output for system bundles
 8.12064-      ((member :image)
 8.12065-       #+allegro "dxl"
 8.12066-       #+(and clisp os-windows) "exe"
 8.12067-       #-(or allegro (and clisp os-windows)) "image")
 8.12068-      ;; NB: on CLASP and ECL these implementations, we better agree with
 8.12069-      ;; (compile-file-type :type bundle-type))
 8.12070-      ((eql :object) ;; the type of a linkable object file
 8.12071-       (os-cond ((os-unix-p)
 8.12072-                 #+clasp "fasp" ;(core:build-extension cmp:*default-object-type*)
 8.12073-                 #-clasp "o")
 8.12074-                ((os-windows-p) (if (featurep '(:or :mingw32 :mingw64)) "o" "obj"))))
 8.12075-      ((member :lib :static-library) ;; the type of a linkable library
 8.12076-       (os-cond ((os-unix-p) "a")
 8.12077-                ((os-windows-p) (if (featurep '(:or :mingw32 :mingw64)) "a" "lib"))))
 8.12078-      ((member :dll :shared-library) ;; the type of a shared library
 8.12079-       (os-cond ((os-macosx-p) "dylib") ((os-unix-p) "so") ((os-windows-p) "dll")))
 8.12080-      ((eql :program) ;; the type of an executable program
 8.12081-       (os-cond ((os-unix-p) nil) ((os-windows-p) "exe")))))
 8.12082-
 8.12083-  ;; Compute the output-files for a given bundle action
 8.12084-  (defun bundle-output-files (o c)
 8.12085-    (let ((bundle-type (bundle-type o)))
 8.12086-      (unless (or (eq bundle-type :no-output-file) ;; NIL already means something regarding type.
 8.12087-                  (and (null (input-files o c)) (not (member bundle-type '(:image :program)))))
 8.12088-        (let ((name (or (component-build-pathname c)
 8.12089-                        (let ((suffix
 8.12090-                               (unless (typep o 'program-op)
 8.12091-                                 ;; "." is no good separator for Logical Pathnames, so we use "--"
 8.12092-                                 (if (operation-monolithic-p o)
 8.12093-                                     "--all-systems"
 8.12094-                                     ;; These use a different type .fasb or .a instead of .fasl
 8.12095-                                     #-(or clasp ecl mkcl) "--system"))))
 8.12096-                          (format nil "~A~@[~A~]" (coerce-filename (component-name c)) suffix))))
 8.12097-              (type (bundle-pathname-type bundle-type)))
 8.12098-          (values (list (subpathname (component-pathname c) name :type type))
 8.12099-                  (eq (class-of o) (coerce-class (component-build-operation c)
 8.12100-                                                 :package :asdf/interface
 8.12101-                                                 :super 'operation
 8.12102-                                                 :error nil)))))))
 8.12103-
 8.12104-  (defmethod output-files ((o bundle-op) (c system))
 8.12105-    (bundle-output-files o c))
 8.12106-
 8.12107-  #-(or clasp ecl mkcl)
 8.12108-  (progn
 8.12109-    (defmethod perform ((o image-op) (c system))
 8.12110-      (dump-image (output-file o c) :executable (typep o 'program-op)))
 8.12111-    (defmethod perform :before ((o program-op) (c system))
 8.12112-      (setf *image-entry-point* (ensure-function (component-entry-point c)))))
 8.12113-
 8.12114-  (defclass compiled-file (file-component)
 8.12115-    ((type :initform #-(or clasp ecl mkcl) (compile-file-type) #+(or clasp ecl mkcl) "fasb"))
 8.12116-    (:documentation "Class for a file that is already compiled,
 8.12117-e.g. as part of the implementation, of an outer build system that calls into ASDF,
 8.12118-or of opaque libraries shipped along the source code."))
 8.12119-
 8.12120-  (defclass precompiled-system (system)
 8.12121-    ((build-pathname :initarg :fasb :initarg :fasl))
 8.12122-    (:documentation "Class For a system that is delivered as a precompiled fasl"))
 8.12123-
 8.12124-  (defclass prebuilt-system (system)
 8.12125-    ((build-pathname :initarg :static-library :initarg :lib
 8.12126-                     :accessor prebuilt-system-static-library))
 8.12127-    (:documentation "Class for a system delivered with a linkable static library (.a/.lib)")))
 8.12128-
 8.12129-
 8.12130-;;;
 8.12131-;;; BUNDLE-OP
 8.12132-;;;
 8.12133-;;; This operation takes all components from one or more systems and
 8.12134-;;; creates a single output file, which may be
 8.12135-;;; a FASL, a statically linked library, a shared library, etc.
 8.12136-;;; The different targets are defined by specialization.
 8.12137-;;;
 8.12138-(when-upgrading (:version "3.2.0")
 8.12139-  ;; Cancel any previously defined method
 8.12140-  (defmethod initialize-instance :after ((instance bundle-op) &rest initargs &key &allow-other-keys)
 8.12141-    (declare (ignore initargs))))
 8.12142-
 8.12143-(with-upgradability ()
 8.12144-  (defgeneric trivial-system-p (component))
 8.12145-
 8.12146-  (defun user-system-p (s)
 8.12147-    (and (typep s 'system)
 8.12148-         (not (builtin-system-p s))
 8.12149-         (not (trivial-system-p s)))))
 8.12150-
 8.12151-(eval-when (#-lispworks :compile-toplevel :load-toplevel :execute)
 8.12152-  (deftype user-system () '(and system (satisfies user-system-p))))
 8.12153-
 8.12154-;;;
 8.12155-;;; First we handle monolithic bundles.
 8.12156-;;; These are standalone systems which contain everything,
 8.12157-;;; including other ASDF systems required by the current one.
 8.12158-;;; A PROGRAM is always monolithic.
 8.12159-;;;
 8.12160-;;; MONOLITHIC SHARED LIBRARIES, PROGRAMS, FASL
 8.12161-;;;
 8.12162-(with-upgradability ()
 8.12163-  (defun direct-dependency-files (o c &key (test 'identity) (key 'output-files) &allow-other-keys)
 8.12164-    ;; This function selects output files from direct dependencies;
 8.12165-    ;; your component-depends-on method must gather the correct dependencies in the correct order.
 8.12166-    (while-collecting (collect)
 8.12167-      (map-direct-dependencies
 8.12168-       o c #'(lambda (sub-o sub-c)
 8.12169-               (loop :for f :in (funcall key sub-o sub-c)
 8.12170-                 :when (funcall test f) :do (collect f))))))
 8.12171-
 8.12172-  (defun pathname-type-equal-function (type)
 8.12173-    #'(lambda (p) (equalp (pathname-type p) type)))
 8.12174-
 8.12175-  (defmethod input-files ((o gather-operation) (c system))
 8.12176-    (unless (eq (bundle-type o) :no-output-file)
 8.12177-      (direct-dependency-files
 8.12178-       o c :key 'output-files
 8.12179-           :test (pathname-type-equal-function (bundle-pathname-type (gather-type o))))))
 8.12180-
 8.12181-  ;; Find the operation that produces a given bundle-type
 8.12182-  (defun select-bundle-operation (type &optional monolithic)
 8.12183-    (ecase type
 8.12184-      ((:dll :shared-library)
 8.12185-       (if monolithic 'monolithic-dll-op 'dll-op))
 8.12186-      ((:lib :static-library)
 8.12187-       (if monolithic 'monolithic-lib-op 'lib-op))
 8.12188-      ((:fasb)
 8.12189-       (if monolithic 'monolithic-compile-bundle-op 'compile-bundle-op))
 8.12190-      ((:image)
 8.12191-       'image-op)
 8.12192-      ((:program)
 8.12193-       'program-op))))
 8.12194-
 8.12195-;;;
 8.12196-;;; LOAD-BUNDLE-OP
 8.12197-;;;
 8.12198-;;; This is like ASDF's LOAD-OP, but using bundle fasl files.
 8.12199-;;;
 8.12200-(with-upgradability ()
 8.12201-  (defmethod component-depends-on ((o load-bundle-op) (c system))
 8.12202-    `((,o ,@(component-sideway-dependencies c))
 8.12203-      (,(if (user-system-p c) 'compile-bundle-op 'load-op) ,c)
 8.12204-      ,@(call-next-method)))
 8.12205-
 8.12206-  (defmethod input-files ((o load-bundle-op) (c system))
 8.12207-    (when (user-system-p c)
 8.12208-      (output-files (find-operation o 'compile-bundle-op) c)))
 8.12209-
 8.12210-  (defmethod perform ((o load-bundle-op) (c system))
 8.12211-    (when (input-files o c)
 8.12212-      (perform-lisp-load-fasl o c)))
 8.12213-
 8.12214-  (defmethod mark-operation-done :after ((o load-bundle-op) (c system))
 8.12215-    (mark-operation-done (find-operation o 'load-op) c)))
 8.12216-
 8.12217-;;;
 8.12218-;;; PRECOMPILED FILES
 8.12219-;;;
 8.12220-;;; This component can be used to distribute ASDF systems in precompiled form.
 8.12221-;;; Only useful when the dependencies have also been precompiled.
 8.12222-;;;
 8.12223-(with-upgradability ()
 8.12224-  (defmethod trivial-system-p ((s system))
 8.12225-    (every #'(lambda (c) (typep c 'compiled-file)) (component-children s)))
 8.12226-
 8.12227-  (defmethod input-files ((o operation) (c compiled-file))
 8.12228-    (list (component-pathname c)))
 8.12229-  (defmethod perform ((o load-op) (c compiled-file))
 8.12230-    (perform-lisp-load-fasl o c))
 8.12231-  (defmethod perform ((o load-source-op) (c compiled-file))
 8.12232-    (perform (find-operation o 'load-op) c))
 8.12233-  (defmethod perform ((o operation) (c compiled-file))
 8.12234-    nil))
 8.12235-
 8.12236-;;;
 8.12237-;;; Pre-built systems
 8.12238-;;;
 8.12239-(with-upgradability ()
 8.12240-  (defmethod trivial-system-p ((s prebuilt-system))
 8.12241-    t)
 8.12242-
 8.12243-  (defmethod perform ((o link-op) (c prebuilt-system))
 8.12244-    nil)
 8.12245-
 8.12246-  (defmethod perform ((o basic-compile-bundle-op) (c prebuilt-system))
 8.12247-    nil)
 8.12248-
 8.12249-  (defmethod perform ((o lib-op) (c prebuilt-system))
 8.12250-    nil)
 8.12251-
 8.12252-  (defmethod perform ((o dll-op) (c prebuilt-system))
 8.12253-    nil)
 8.12254-
 8.12255-  (defmethod component-depends-on ((o gather-operation) (c prebuilt-system))
 8.12256-    nil)
 8.12257-
 8.12258-  (defmethod output-files ((o lib-op) (c prebuilt-system))
 8.12259-    (values (list (prebuilt-system-static-library c)) t)))
 8.12260-
 8.12261-
 8.12262-;;;
 8.12263-;;; PREBUILT SYSTEM CREATOR
 8.12264-;;;
 8.12265-(with-upgradability ()
 8.12266-  (defmethod output-files ((o deliver-asd-op) (s system))
 8.12267-    (list (make-pathname :name (coerce-filename (component-name s)) :type "asd"
 8.12268-                         :defaults (component-pathname s))))
 8.12269-
 8.12270-  ;; because of name collisions between the output files of different
 8.12271-  ;; subclasses of DELIVER-ASD-OP, we cannot trust the file system to
 8.12272-  ;; tell us if the output file is up-to-date, so just treat the
 8.12273-  ;; operation as never being done.
 8.12274-  (defmethod operation-done-p ((o deliver-asd-op) (s system))
 8.12275-    (declare (ignorable o s))
 8.12276-    nil)
 8.12277-
 8.12278-  (defun space-for-crlf (s)
 8.12279-    (substitute-if #\space #'(lambda (x) (find x +crlf+)) s))
 8.12280-
 8.12281-  (defmethod perform ((o deliver-asd-op) (s system))
 8.12282-    "Write an ASDF system definition for loading S as a delivered system."
 8.12283-    (let* ((inputs (input-files o s))
 8.12284-           (fasl (first inputs))
 8.12285-           (library (second inputs))
 8.12286-           (asd (output-file o s))
 8.12287-           (name (if (and fasl asd) (pathname-name asd) (return-from perform)))
 8.12288-           (version (component-version s))
 8.12289-           (dependencies
 8.12290-             (if (operation-monolithic-p o)
 8.12291-                 ;; We want only dependencies, and we use basic-load-op rather than load-op so that
 8.12292-                 ;; this will keep working on systems that load dependencies with load-bundle-op
 8.12293-                 (remove-if-not 'builtin-system-p
 8.12294-                                (required-components s :component-type 'system
 8.12295-                                                       :keep-operation 'basic-load-op))
 8.12296-                 (while-collecting (x) ;; resolve the sideway-dependencies of s
 8.12297-                   (map-direct-dependencies
 8.12298-                    'prepare-op s
 8.12299-                    #'(lambda (o c)
 8.12300-                        (when (and (typep o 'load-op) (typep c 'system))
 8.12301-                          (x c)))))))
 8.12302-           (depends-on (mapcar 'coerce-name dependencies)))
 8.12303-      (when (pathname-equal asd (system-source-file s))
 8.12304-        (cerror "overwrite the asd file"
 8.12305-                "~/asdf-action:format-action/ is going to overwrite the system definition file ~S ~
 8.12306-which is probably not what you want; you probably need to tweak your output translations."
 8.12307-                (cons o s) asd))
 8.12308-      (with-open-file (s asd :direction :output :if-exists :supersede
 8.12309-                             :if-does-not-exist :create)
 8.12310-        (format s ";;; Prebuilt~:[~; monolithic~] ASDF definition for system ~A~%"
 8.12311-                (operation-monolithic-p o) name)
 8.12312-        ;; this can cause bugs in cases where one of the functions returns a multi-line
 8.12313-        ;; string
 8.12314-        (let ((description-string (format nil ";;; Built for ~A ~A on a ~A/~A ~A"
 8.12315-                    (lisp-implementation-type)
 8.12316-                    (lisp-implementation-version)
 8.12317-                    (software-type)
 8.12318-                    (machine-type)
 8.12319-                    (software-version))))
 8.12320-          ;; ensure the whole thing is on one line
 8.12321-          (println (space-for-crlf description-string) s))
 8.12322-        (let ((*package* (find-package :asdf-user)))
 8.12323-          (pprint `(defsystem ,name
 8.12324-                     :class prebuilt-system
 8.12325-                     :version ,version
 8.12326-                     :depends-on ,depends-on
 8.12327-                     :components ((:compiled-file ,(pathname-name fasl)))
 8.12328-                     ,@(when library `(:lib ,(file-namestring library))))
 8.12329-                  s)
 8.12330-          (terpri s)))))
 8.12331-
 8.12332-  #-(or clasp ecl mkcl)
 8.12333-  (defmethod perform ((o basic-compile-bundle-op) (c system))
 8.12334-    (let* ((input-files (input-files o c))
 8.12335-           (fasl-files (remove (compile-file-type) input-files :key #'pathname-type :test-not #'equalp))
 8.12336-           (non-fasl-files (remove (compile-file-type) input-files :key #'pathname-type :test #'equalp))
 8.12337-           (output-files (output-files o c)) ; can't use OUTPUT-FILE fn because possibility it's NIL
 8.12338-           (output-file (first output-files)))
 8.12339-      (assert (eq (not input-files) (not output-files)))
 8.12340-      (when input-files
 8.12341-        (when non-fasl-files
 8.12342-          (error "On ~A, asdf/bundle can only bundle FASL files, but these were also produced: ~S"
 8.12343-                 (implementation-type) non-fasl-files))
 8.12344-        (when (or (prologue-code c) (epilogue-code c))
 8.12345-          (error "prologue-code and epilogue-code are not supported on ~A"
 8.12346-                 (implementation-type)))
 8.12347-        (with-staging-pathname (output-file)
 8.12348-          (combine-fasls fasl-files output-file)))))
 8.12349-
 8.12350-  (defmethod input-files ((o load-op) (s precompiled-system))
 8.12351-    (bundle-output-files (find-operation o 'compile-bundle-op) s))
 8.12352-
 8.12353-  (defmethod perform ((o load-op) (s precompiled-system))
 8.12354-    (perform-lisp-load-fasl o s))
 8.12355-
 8.12356-  (defmethod component-depends-on ((o load-bundle-op) (s precompiled-system))
 8.12357-    `((load-op ,s) ,@(call-next-method))))
 8.12358-
 8.12359-#| ;; Example use:
 8.12360-(asdf:defsystem :precompiled-asdf-utils :class asdf::precompiled-system :fasl (asdf:apply-output-translations (asdf:system-relative-pathname :asdf-utils "asdf-utils.system.fasl")))
 8.12361-(asdf:load-system :precompiled-asdf-utils)
 8.12362-|#
 8.12363-
 8.12364-#+(or clasp ecl mkcl)
 8.12365-(with-upgradability ()
 8.12366-  (defun system-module-pathname (module)
 8.12367-    (let ((name (coerce-name module)))
 8.12368-      (some
 8.12369-       'file-exists-p
 8.12370-       (list
 8.12371-        #+clasp (compile-file-pathname (make-pathname :name name :defaults "sys:") :output-type :object)
 8.12372-        #+ecl (compile-file-pathname (make-pathname :name name :defaults "sys:") :type :lib)
 8.12373-        #+ecl (compile-file-pathname (make-pathname :name (strcat "lib" name) :defaults "sys:") :type :lib)
 8.12374-        #+ecl (compile-file-pathname (make-pathname :name name :defaults "sys:") :type :object)
 8.12375-        #+mkcl (make-pathname :name name :type (bundle-pathname-type :lib) :defaults #p"sys:")
 8.12376-        #+mkcl (make-pathname :name name :type (bundle-pathname-type :lib) :defaults #p"sys:contrib;")))))
 8.12377-
 8.12378-  (defun make-prebuilt-system (name &optional (pathname (system-module-pathname name)))
 8.12379-    "Creates a prebuilt-system if PATHNAME isn't NIL."
 8.12380-    (when pathname
 8.12381-      (make-instance 'prebuilt-system
 8.12382-                     :name (coerce-name name)
 8.12383-                     :static-library (resolve-symlinks* pathname))))
 8.12384-
 8.12385-  (defun linkable-system (x)
 8.12386-    (or ;; If the system is available as source, use it.
 8.12387-        (if-let (s (find-system x))
 8.12388-          (and (output-files 'lib-op s) s))
 8.12389-        ;; If an ASDF upgrade is available from source, but not a UIOP upgrade to that,
 8.12390-        ;; then use the asdf/driver system instead of
 8.12391-        ;; the UIOP that was disabled by check-not-old-asdf-system.
 8.12392-        (if-let (s (and (equal (coerce-name x) "uiop")
 8.12393-                        (output-files 'lib-op "asdf")
 8.12394-                        (find-system "asdf/driver")))
 8.12395-          (and (output-files 'lib-op s) s))
 8.12396-        ;; If there was no source upgrade, look for modules provided by the implementation.
 8.12397-        (if-let (p (system-module-pathname (coerce-name x)))
 8.12398-          (make-prebuilt-system x p))))
 8.12399-
 8.12400-  (defmethod component-depends-on :around ((o image-op) (c system))
 8.12401-    (let* ((next (call-next-method))
 8.12402-           (deps (make-hash-table :test 'equal))
 8.12403-           (linkable (loop :for (do . dcs) :in next :collect
 8.12404-                       (cons do
 8.12405-                             (loop :for dc :in dcs
 8.12406-                               :for dep = (and dc (resolve-dependency-spec c dc))
 8.12407-                               :when dep
 8.12408-                               :do (setf (gethash (coerce-name (component-system dep)) deps) t)
 8.12409-                               :collect (or (and (typep dep 'system) (linkable-system dep)) dep))))))
 8.12410-        `((lib-op
 8.12411-           ,@(unless (no-uiop c)
 8.12412-               (list (linkable-system "cmp")
 8.12413-                     (unless (or (and (gethash "uiop" deps) (linkable-system "uiop"))
 8.12414-                                 (and (gethash "asdf" deps) (linkable-system "asdf")))
 8.12415-                       (or (linkable-system "uiop")
 8.12416-                           (linkable-system "asdf")
 8.12417-                           "asdf")))))
 8.12418-          ,@linkable)))
 8.12419-
 8.12420-  (defmethod perform ((o link-op) (c system))
 8.12421-    (let* ((object-files (input-files o c))
 8.12422-           (output (output-files o c))
 8.12423-           (bundle (first output))
 8.12424-           (programp (typep o 'program-op))
 8.12425-           (kind (bundle-type o)))
 8.12426-      (when output
 8.12427-        (apply 'create-image
 8.12428-               bundle (append
 8.12429-                       (when programp (prefix-lisp-object-files c))
 8.12430-                       object-files
 8.12431-                       (when programp (postfix-lisp-object-files c)))
 8.12432-               :kind kind
 8.12433-               :prologue-code (when programp (prologue-code c))
 8.12434-               :epilogue-code (when programp (epilogue-code c))
 8.12435-               :build-args (when programp (extra-build-args c))
 8.12436-               :extra-object-files (when programp (extra-object-files c))
 8.12437-               :no-uiop (no-uiop c)
 8.12438-               (when programp `(:entry-point ,(component-entry-point c))))))))
 8.12439-;;;; -------------------------------------------------------------------------
 8.12440-;;;; Concatenate-source
 8.12441-
 8.12442-(uiop/package:define-package :asdf/concatenate-source
 8.12443-  (:recycle :asdf/concatenate-source :asdf)
 8.12444-  (:use :uiop/common-lisp :uiop :asdf/upgrade
 8.12445-   :asdf/component :asdf/operation
 8.12446-   :asdf/system
 8.12447-   :asdf/action :asdf/lisp-action :asdf/plan :asdf/bundle)
 8.12448-  (:export
 8.12449-   #:concatenate-source-op
 8.12450-   #:load-concatenated-source-op
 8.12451-   #:compile-concatenated-source-op
 8.12452-   #:load-compiled-concatenated-source-op
 8.12453-   #:monolithic-concatenate-source-op
 8.12454-   #:monolithic-load-concatenated-source-op
 8.12455-   #:monolithic-compile-concatenated-source-op
 8.12456-   #:monolithic-load-compiled-concatenated-source-op))
 8.12457-(in-package :asdf/concatenate-source)
 8.12458-
 8.12459-;;;
 8.12460-;;; Concatenate sources
 8.12461-;;;
 8.12462-(with-upgradability ()
 8.12463-  ;; Base classes for both regular and monolithic concatenate-source operations
 8.12464-  (defclass basic-concatenate-source-op (bundle-op) ())
 8.12465-  (defmethod bundle-type ((o basic-concatenate-source-op)) "lisp")
 8.12466-  (defclass basic-load-concatenated-source-op (basic-load-op selfward-operation) ())
 8.12467-  (defclass basic-compile-concatenated-source-op (basic-compile-op selfward-operation) ())
 8.12468-  (defclass basic-load-compiled-concatenated-source-op (basic-load-op selfward-operation) ())
 8.12469-
 8.12470-  ;; Regular concatenate-source operations
 8.12471-  (defclass concatenate-source-op (basic-concatenate-source-op non-propagating-operation) ()
 8.12472-    (:documentation "Operation to concatenate all sources in a system into a single file"))
 8.12473-  (defclass load-concatenated-source-op (basic-load-concatenated-source-op)
 8.12474-    ((selfward-operation :initform '(prepare-op concatenate-source-op) :allocation :class))
 8.12475-    (:documentation "Operation to load the result of concatenate-source-op as source"))
 8.12476-  (defclass compile-concatenated-source-op (basic-compile-concatenated-source-op)
 8.12477-    ((selfward-operation :initform '(prepare-op concatenate-source-op) :allocation :class))
 8.12478-    (:documentation "Operation to compile the result of concatenate-source-op"))
 8.12479-  (defclass load-compiled-concatenated-source-op (basic-load-compiled-concatenated-source-op)
 8.12480-    ((selfward-operation :initform '(prepare-op compile-concatenated-source-op) :allocation :class))
 8.12481-    (:documentation "Operation to load the result of compile-concatenated-source-op"))
 8.12482-
 8.12483-  (defclass monolithic-concatenate-source-op
 8.12484-      (basic-concatenate-source-op monolithic-bundle-op non-propagating-operation) ()
 8.12485-    (:documentation "Operation to concatenate all sources in a system and its dependencies
 8.12486-into a single file"))
 8.12487-  (defclass monolithic-load-concatenated-source-op (basic-load-concatenated-source-op)
 8.12488-    ((selfward-operation :initform 'monolithic-concatenate-source-op :allocation :class))
 8.12489-    (:documentation "Operation to load the result of monolithic-concatenate-source-op as source"))
 8.12490-  (defclass monolithic-compile-concatenated-source-op (basic-compile-concatenated-source-op)
 8.12491-    ((selfward-operation :initform 'monolithic-concatenate-source-op :allocation :class))
 8.12492-    (:documentation "Operation to compile the result of monolithic-concatenate-source-op"))
 8.12493-  (defclass monolithic-load-compiled-concatenated-source-op
 8.12494-      (basic-load-compiled-concatenated-source-op)
 8.12495-    ((selfward-operation :initform 'monolithic-compile-concatenated-source-op :allocation :class))
 8.12496-    (:documentation "Operation to load the result of monolithic-compile-concatenated-source-op"))
 8.12497-
 8.12498-  (defmethod input-files ((operation basic-concatenate-source-op) (s system))
 8.12499-    (loop :with encoding = (or (component-encoding s) *default-encoding*)
 8.12500-          :with other-encodings = '()
 8.12501-          :with around-compile = (around-compile-hook s)
 8.12502-          :with other-around-compile = '()
 8.12503-          :for c :in (required-components  ;; see note about similar call to required-components
 8.12504-                      s :goal-operation 'load-op ;;  in bundle.lisp
 8.12505-                        :keep-operation 'basic-compile-op
 8.12506-                        :other-systems (operation-monolithic-p operation))
 8.12507-          :append
 8.12508-          (when (typep c 'cl-source-file)
 8.12509-            (let ((e (component-encoding c)))
 8.12510-              (unless (or (equal e encoding)
 8.12511-                          (and (equal e :ASCII) (equal encoding :UTF-8)))
 8.12512-                (let ((a (assoc e other-encodings)))
 8.12513-                  (if a (push (component-find-path c) (cdr a))
 8.12514-                      (push (list e (component-find-path c)) other-encodings)))))
 8.12515-            (unless (equal around-compile (around-compile-hook c))
 8.12516-              (push (component-find-path c) other-around-compile))
 8.12517-            (input-files (make-operation 'compile-op) c)) :into inputs
 8.12518-          :finally
 8.12519-             (when other-encodings
 8.12520-               (warn "~S uses encoding ~A but has sources that use these encodings:~{ ~A~}"
 8.12521-                     operation encoding
 8.12522-                     (mapcar #'(lambda (x) (cons (car x) (list (reverse (cdr x)))))
 8.12523-                             other-encodings)))
 8.12524-             (when other-around-compile
 8.12525-               (warn "~S uses around-compile hook ~A but has sources that use these hooks: ~A"
 8.12526-                     operation around-compile other-around-compile))
 8.12527-             (return inputs)))
 8.12528-  (defmethod output-files ((o basic-compile-concatenated-source-op) (s system))
 8.12529-    (lisp-compilation-output-files o s))
 8.12530-
 8.12531-  (defmethod perform ((o basic-concatenate-source-op) (s system))
 8.12532-    (let* ((ins (input-files o s))
 8.12533-           (out (output-file o s))
 8.12534-           (tmp (tmpize-pathname out)))
 8.12535-      (concatenate-files ins tmp)
 8.12536-      (rename-file-overwriting-target tmp out)))
 8.12537-  (defmethod perform ((o basic-load-concatenated-source-op) (s system))
 8.12538-    (perform-lisp-load-source o s))
 8.12539-  (defmethod perform ((o basic-compile-concatenated-source-op) (s system))
 8.12540-    (perform-lisp-compilation o s))
 8.12541-  (defmethod perform ((o basic-load-compiled-concatenated-source-op) (s system))
 8.12542-    (perform-lisp-load-fasl o s)))
 8.12543-
 8.12544-;;;; -------------------------------------------------------------------------
 8.12545-;;;; Package systems in the style of quick-build or faslpath
 8.12546-
 8.12547-(uiop:define-package :asdf/package-inferred-system
 8.12548-  (:recycle :asdf/package-inferred-system :asdf/package-system :asdf)
 8.12549-  (:use :uiop/common-lisp :uiop
 8.12550-        :asdf/upgrade :asdf/session
 8.12551-        :asdf/component :asdf/system :asdf/system-registry :asdf/lisp-action
 8.12552-        :asdf/parse-defsystem)
 8.12553-  (:export
 8.12554-   #:package-inferred-system #:sysdef-package-inferred-system-search
 8.12555-   #:package-system ;; backward compatibility only. To be removed.
 8.12556-   #:register-system-packages
 8.12557-   #:*defpackage-forms* #:*package-inferred-systems* #:package-inferred-system-missing-package-error))
 8.12558-(in-package :asdf/package-inferred-system)
 8.12559-
 8.12560-(with-upgradability ()
 8.12561-  ;; The names of the recognized defpackage forms.
 8.12562-  (defparameter *defpackage-forms* '(defpackage define-package))
 8.12563-
 8.12564-  (defun initial-package-inferred-systems-table ()
 8.12565-    ;; Mark all existing packages are preloaded.
 8.12566-    (let ((h (make-hash-table :test 'equal)))
 8.12567-      (dolist (p (list-all-packages))
 8.12568-        (dolist (n (package-names p))
 8.12569-          (setf (gethash n h) t)))
 8.12570-      h))
 8.12571-
 8.12572-  ;; Mapping from package names to systems that provide them.
 8.12573-  (defvar *package-inferred-systems* (initial-package-inferred-systems-table))
 8.12574-
 8.12575-  (defclass package-inferred-system (system)
 8.12576-    ()
 8.12577-    (:documentation "Class for primary systems for which secondary systems are automatically
 8.12578-in the one-file, one-file, one-system style: system names are mapped to files under the primary
 8.12579-system's system-source-directory, dependencies are inferred from the first defpackage form in
 8.12580-every such file"))
 8.12581-
 8.12582-  ;; DEPRECATED. For backward compatibility only. To be removed in an upcoming release:
 8.12583-  (defclass package-system (package-inferred-system) ())
 8.12584-
 8.12585-  ;; Is a given form recognizable as a defpackage form?
 8.12586-  (defun defpackage-form-p (form)
 8.12587-    (and (consp form)
 8.12588-         (member (car form) *defpackage-forms*)))
 8.12589-
 8.12590-  ;; Find the first defpackage form in a stream, if any
 8.12591-  (defun stream-defpackage-form (stream)
 8.12592-    (loop :for form = (read stream nil nil) :while form
 8.12593-          :when (defpackage-form-p form) :return form))
 8.12594-
 8.12595-  (defun file-defpackage-form (file)
 8.12596-    "Return the first DEFPACKAGE form in FILE."
 8.12597-    (with-input-file (f file)
 8.12598-      (stream-defpackage-form f)))
 8.12599-
 8.12600-  (define-condition package-inferred-system-missing-package-error (system-definition-error)
 8.12601-    ((system :initarg :system :reader error-system)
 8.12602-     (pathname :initarg :pathname :reader error-pathname))
 8.12603-    (:report (lambda (c s)
 8.12604-               (format s (compatfmt "~@<No package form found while ~
 8.12605-                                     trying to define package-inferred-system ~A from file ~A~>")
 8.12606-                       (error-system c) (error-pathname c)))))
 8.12607-
 8.12608-  (defun package-dependencies (defpackage-form)
 8.12609-    "Return a list of packages depended on by the package
 8.12610-defined in DEFPACKAGE-FORM.  A package is depended upon if
 8.12611-the DEFPACKAGE-FORM uses it or imports a symbol from it."
 8.12612-    (assert (defpackage-form-p defpackage-form))
 8.12613-    (remove-duplicates
 8.12614-     (while-collecting (dep)
 8.12615-       (loop :for (option . arguments) :in (cddr defpackage-form) :do
 8.12616-         (ecase option
 8.12617-           ((:use :mix :reexport :use-reexport :mix-reexport)
 8.12618-            (dolist (p arguments) (dep (string p))))
 8.12619-           ((:import-from :shadowing-import-from)
 8.12620-            (dep (string (first arguments))))
 8.12621-           #+package-local-nicknames
 8.12622-           ((:local-nicknames)
 8.12623-            (loop :for (nil actual-package-name) :in arguments :do
 8.12624-              (dep (string actual-package-name))))
 8.12625-           ((:nicknames :documentation :shadow :export :intern :unintern :recycle)))))
 8.12626-     :from-end t :test 'equal))
 8.12627-
 8.12628-  (defun package-designator-name (package)
 8.12629-    "Normalize a package designator to a string"
 8.12630-    (etypecase package
 8.12631-      (package (package-name package))
 8.12632-      (string package)
 8.12633-      (symbol (string package))))
 8.12634-
 8.12635-  (defun register-system-packages (system packages)
 8.12636-    "Register SYSTEM as providing PACKAGES."
 8.12637-    (let ((name (or (eq system t) (coerce-name system))))
 8.12638-      (dolist (p (ensure-list packages))
 8.12639-        (setf (gethash (package-designator-name p) *package-inferred-systems*) name))))
 8.12640-
 8.12641-  (defun package-name-system (package-name)
 8.12642-    "Return the name of the SYSTEM providing PACKAGE-NAME, if such exists,
 8.12643-otherwise return a default system name computed from PACKAGE-NAME."
 8.12644-    (check-type package-name string)
 8.12645-    (or (gethash package-name *package-inferred-systems*)
 8.12646-        (string-downcase package-name)))
 8.12647-
 8.12648-  ;; Given a file in package-inferred-system style, find its dependencies
 8.12649-  (defun package-inferred-system-file-dependencies (file &optional system)
 8.12650-    (if-let (defpackage-form (file-defpackage-form file))
 8.12651-      (remove t (mapcar 'package-name-system (package-dependencies defpackage-form)))
 8.12652-      (error 'package-inferred-system-missing-package-error :system system :pathname file)))
 8.12653-
 8.12654-  ;; Given package-inferred-system object, check whether its specification matches
 8.12655-  ;; the provided parameters
 8.12656-  (defun same-package-inferred-system-p (system name directory subpath around-compile dependencies)
 8.12657-    (and (eq (type-of system) 'package-inferred-system)
 8.12658-         (equal (component-name system) name)
 8.12659-         (pathname-equal directory (component-pathname system))
 8.12660-         (equal dependencies (component-sideway-dependencies system))
 8.12661-         (equal around-compile (around-compile-hook system))
 8.12662-         (let ((children (component-children system)))
 8.12663-           (and (length=n-p children 1)
 8.12664-                (let ((child (first children)))
 8.12665-                  (and (eq (type-of child) 'cl-source-file)
 8.12666-                       (equal (component-name child) "lisp")
 8.12667-                       (and (slot-boundp child 'relative-pathname)
 8.12668-                            (equal (slot-value child 'relative-pathname) subpath))))))))
 8.12669-
 8.12670-  ;; sysdef search function to push into *system-definition-search-functions*
 8.12671-  (defun sysdef-package-inferred-system-search (system-name)
 8.12672-  "Takes SYSTEM-NAME and returns an initialized SYSTEM object, or NIL.  Made to be added to
 8.12673-*SYSTEM-DEFINITION-SEARCH-FUNCTIONS*."
 8.12674-    (let ((primary (primary-system-name system-name)))
 8.12675-      ;; this function ONLY does something if the primary system name is NOT the same as
 8.12676-      ;; SYSTEM-NAME.  It is used to find the systems with names that are relative to
 8.12677-      ;; the primary system's name, and that are not explicitly specified in the system
 8.12678-      ;; definition
 8.12679-      (unless (equal primary system-name)
 8.12680-        (let ((top (find-system primary nil)))
 8.12681-          (when (typep top 'package-inferred-system)
 8.12682-            (if-let (dir (component-pathname top))
 8.12683-              (let* ((sub (subseq system-name (1+ (length primary))))
 8.12684-                     (component-type (class-for-type top :file))
 8.12685-                     (file-type (file-type (make-instance component-type)))
 8.12686-                     (f (probe-file* (subpathname dir sub :type file-type)
 8.12687-                                     :truename *resolve-symlinks*)))
 8.12688-                (when (file-pathname-p f)
 8.12689-                  (let ((dependencies (package-inferred-system-file-dependencies f system-name))
 8.12690-                        (previous (registered-system system-name))
 8.12691-                        (around-compile (around-compile-hook top)))
 8.12692-                    (if (same-package-inferred-system-p previous system-name dir sub around-compile dependencies)
 8.12693-                        previous
 8.12694-                        (eval `(defsystem ,system-name
 8.12695-                                 :class package-inferred-system
 8.12696-                                 :default-component-class ,component-type
 8.12697-                                 :source-file ,(system-source-file top)
 8.12698-                                 :pathname ,dir
 8.12699-                                 :depends-on ,dependencies
 8.12700-                                 :around-compile ,around-compile
 8.12701-                                 :components ((,component-type file-type :pathname ,sub)))))))))))))))
 8.12702-
 8.12703-(with-upgradability ()
 8.12704-  (pushnew 'sysdef-package-inferred-system-search *system-definition-search-functions*)
 8.12705-  (setf *system-definition-search-functions*
 8.12706-        (remove (find-symbol* :sysdef-package-system-search :asdf/package-system nil)
 8.12707-                *system-definition-search-functions*)))
 8.12708-;;;; ---------------------------------------------------------------------------
 8.12709-;;;; asdf-output-translations
 8.12710-
 8.12711-(uiop/package:define-package :asdf/output-translations
 8.12712-  (:recycle :asdf/output-translations :asdf)
 8.12713-  (:use :uiop/common-lisp :uiop :asdf/upgrade)
 8.12714-  (:export
 8.12715-   #:*output-translations* #:*output-translations-parameter*
 8.12716-   #:invalid-output-translation
 8.12717-   #:output-translations #:output-translations-initialized-p
 8.12718-   #:initialize-output-translations #:clear-output-translations
 8.12719-   #:disable-output-translations #:ensure-output-translations
 8.12720-   #:apply-output-translations
 8.12721-   #:validate-output-translations-directive #:validate-output-translations-form
 8.12722-   #:validate-output-translations-file #:validate-output-translations-directory
 8.12723-   #:parse-output-translations-string #:wrapping-output-translations
 8.12724-   #:user-output-translations-pathname #:system-output-translations-pathname
 8.12725-   #:user-output-translations-directory-pathname #:system-output-translations-directory-pathname
 8.12726-   #:environment-output-translations #:process-output-translations
 8.12727-   #:compute-output-translations
 8.12728-   #+abcl #:translate-jar-pathname
 8.12729-   ))
 8.12730-(in-package :asdf/output-translations)
 8.12731-
 8.12732-;; (setf output-translations) between 2.27 and 3.0.3 was using a defsetf macro
 8.12733-;; for the sake of obsolete versions of GCL 2.6. Make sure it doesn't come to haunt us.
 8.12734-(when-upgrading (:version "3.1.2") (fmakunbound '(setf output-translations)))
 8.12735-
 8.12736-(with-upgradability ()
 8.12737-  (define-condition invalid-output-translation (invalid-configuration warning)
 8.12738-    ((format :initform (compatfmt "~@<Invalid asdf output-translation ~S~@[ in ~S~]~@{ ~@?~}~@:>"))))
 8.12739-
 8.12740-  (defvar *output-translations* ()
 8.12741-    "Either NIL (for uninitialized), or a list of one element,
 8.12742-said element itself being a sorted list of mappings.
 8.12743-Each mapping is a pair of a source pathname and destination pathname,
 8.12744-and the order is by decreasing length of namestring of the source pathname.")
 8.12745-
 8.12746-  (defun output-translations ()
 8.12747-    "Return the configured output-translations, if any"
 8.12748-    (car *output-translations*))
 8.12749-
 8.12750-  ;; Set the output-translations, by sorting the provided new-value.
 8.12751-  (defun set-output-translations (new-value)
 8.12752-    (setf *output-translations*
 8.12753-          (list
 8.12754-           (stable-sort (copy-list new-value) #'>
 8.12755-                        :key #'(lambda (x)
 8.12756-                                 (etypecase (car x)
 8.12757-                                   ((eql t) -1)
 8.12758-                                   (pathname
 8.12759-                                    (let ((directory
 8.12760-                                           (normalize-pathname-directory-component
 8.12761-                                            (pathname-directory (car x)))))
 8.12762-                                      (if (listp directory) (length directory) 0))))))))
 8.12763-    new-value)
 8.12764-  (defun (setf output-translations) (new-value) (set-output-translations new-value))
 8.12765-
 8.12766-  (defun output-translations-initialized-p ()
 8.12767-    "Have the output-translations been initialized yet?"
 8.12768-    (and *output-translations* t))
 8.12769-
 8.12770-  (defun clear-output-translations ()
 8.12771-    "Undoes any initialization of the output translations."
 8.12772-    (setf *output-translations* '())
 8.12773-    (values))
 8.12774-  (register-clear-configuration-hook 'clear-output-translations)
 8.12775-
 8.12776-
 8.12777-  ;;; Validation of the configuration directives...
 8.12778-
 8.12779-  (defun validate-output-translations-directive (directive)
 8.12780-    (or (member directive '(:enable-user-cache :disable-cache nil))
 8.12781-        (and (consp directive)
 8.12782-             (or (and (length=n-p directive 2)
 8.12783-                      (or (and (eq (first directive) :include)
 8.12784-                               (typep (second directive) '(or string pathname null)))
 8.12785-                          (and (location-designator-p (first directive))
 8.12786-                               (or (location-designator-p (second directive))
 8.12787-                                   (location-function-p (second directive))))))
 8.12788-                 (and (length=n-p directive 1)
 8.12789-                      (location-designator-p (first directive)))))))
 8.12790-
 8.12791-  (defun validate-output-translations-form (form &key location)
 8.12792-    (validate-configuration-form
 8.12793-     form
 8.12794-     :output-translations
 8.12795-     'validate-output-translations-directive
 8.12796-     :location location :invalid-form-reporter 'invalid-output-translation))
 8.12797-
 8.12798-  (defun validate-output-translations-file (file)
 8.12799-    (validate-configuration-file
 8.12800-     file 'validate-output-translations-form :description "output translations"))
 8.12801-
 8.12802-  (defun validate-output-translations-directory (directory)
 8.12803-    (validate-configuration-directory
 8.12804-     directory :output-translations 'validate-output-translations-directive
 8.12805-               :invalid-form-reporter 'invalid-output-translation))
 8.12806-
 8.12807-
 8.12808-  ;;; Parse the ASDF_OUTPUT_TRANSLATIONS environment variable and/or some file contents
 8.12809-  (defun parse-output-translations-string (string &key location)
 8.12810-    (cond
 8.12811-      ((or (null string) (equal string ""))
 8.12812-       '(:output-translations :inherit-configuration))
 8.12813-      ((not (stringp string))
 8.12814-       (error (compatfmt "~@<Environment string isn't: ~3i~_~S~@:>") string))
 8.12815-      ((eql (char string 0) #\")
 8.12816-       (parse-output-translations-string (read-from-string string) :location location))
 8.12817-      ((eql (char string 0) #\()
 8.12818-       (validate-output-translations-form (read-from-string string) :location location))
 8.12819-      (t
 8.12820-       (loop
 8.12821-         :with inherit = nil
 8.12822-         :with directives = ()
 8.12823-         :with start = 0
 8.12824-         :with end = (length string)
 8.12825-         :with source = nil
 8.12826-         :with separator = (inter-directory-separator)
 8.12827-         :for i = (or (position separator string :start start) end) :do
 8.12828-           (let ((s (subseq string start i)))
 8.12829-             (cond
 8.12830-               (source
 8.12831-                (push (list source (if (equal "" s) nil s)) directives)
 8.12832-                (setf source nil))
 8.12833-               ((equal "" s)
 8.12834-                (when inherit
 8.12835-                  (error (compatfmt "~@<Only one inherited configuration allowed: ~3i~_~S~@:>")
 8.12836-                         string))
 8.12837-                (setf inherit t)
 8.12838-                (push :inherit-configuration directives))
 8.12839-               (t
 8.12840-                (setf source s)))
 8.12841-             (setf start (1+ i))
 8.12842-             (when (> start end)
 8.12843-               (when source
 8.12844-                 (error (compatfmt "~@<Uneven number of components in source to destination mapping: ~3i~_~S~@:>")
 8.12845-                        string))
 8.12846-               (unless inherit
 8.12847-                 (push :ignore-inherited-configuration directives))
 8.12848-               (return `(:output-translations ,@(nreverse directives)))))))))
 8.12849-
 8.12850-
 8.12851-  ;; The default sources of configuration for output-translations
 8.12852-  (defparameter* *default-output-translations*
 8.12853-    '(environment-output-translations
 8.12854-      user-output-translations-pathname
 8.12855-      user-output-translations-directory-pathname
 8.12856-      system-output-translations-pathname
 8.12857-      system-output-translations-directory-pathname))
 8.12858-
 8.12859-  ;; Compulsory implementation-dependent wrapping for the translations:
 8.12860-  ;; handle implementation-provided systems.
 8.12861-  (defun wrapping-output-translations ()
 8.12862-    `(:output-translations
 8.12863-    ;; Some implementations have precompiled ASDF systems,
 8.12864-    ;; so we must disable translations for implementation paths.
 8.12865-      #+(or clasp #|clozure|# ecl mkcl sbcl)
 8.12866-      ,@(let ((h (resolve-symlinks* (lisp-implementation-directory))))
 8.12867-          (when h `(((,h ,*wild-path*) ()))))
 8.12868-      #+mkcl (,(translate-logical-pathname "CONTRIB:") ())
 8.12869-      ;; All-import, here is where we want user stuff to be:
 8.12870-      :inherit-configuration
 8.12871-      ;; These are for convenience, and can be overridden by the user:
 8.12872-      #+abcl (#p"/___jar___file___root___/**/*.*" (:user-cache #p"**/*.*"))
 8.12873-      #+abcl (#p"jar:file:/**/*.jar!/**/*.*" (:function translate-jar-pathname))
 8.12874-      ;; We enable the user cache by default, and here is the place we do:
 8.12875-      :enable-user-cache))
 8.12876-
 8.12877-  ;; Relative pathnames of output-translations configuration to XDG configuration directory
 8.12878-  (defparameter *output-translations-file* (parse-unix-namestring "common-lisp/asdf-output-translations.conf"))
 8.12879-  (defparameter *output-translations-directory* (parse-unix-namestring "common-lisp/asdf-output-translations.conf.d/"))
 8.12880-
 8.12881-  ;; Locating various configuration pathnames, depending on input or output intent.
 8.12882-  (defun user-output-translations-pathname (&key (direction :input))
 8.12883-    (xdg-config-pathname *output-translations-file* direction))
 8.12884-  (defun system-output-translations-pathname (&key (direction :input))
 8.12885-    (find-preferred-file (system-config-pathnames *output-translations-file*)
 8.12886-                         :direction direction))
 8.12887-  (defun user-output-translations-directory-pathname (&key (direction :input))
 8.12888-    (xdg-config-pathname *output-translations-directory* direction))
 8.12889-  (defun system-output-translations-directory-pathname (&key (direction :input))
 8.12890-    (find-preferred-file (system-config-pathnames *output-translations-directory*)
 8.12891-                         :direction direction))
 8.12892-  (defun environment-output-translations ()
 8.12893-    (getenv "ASDF_OUTPUT_TRANSLATIONS"))
 8.12894-
 8.12895-
 8.12896-  ;;; Processing the configuration.
 8.12897-
 8.12898-  (defgeneric process-output-translations (spec &key inherit collect))
 8.12899-
 8.12900-  (defun inherit-output-translations (inherit &key collect)
 8.12901-    (when inherit
 8.12902-      (process-output-translations (first inherit) :collect collect :inherit (rest inherit))))
 8.12903-
 8.12904-  (defun process-output-translations-directive (directive &key inherit collect)
 8.12905-    (if (atom directive)
 8.12906-        (ecase directive
 8.12907-          ((:enable-user-cache)
 8.12908-           (process-output-translations-directive '(t :user-cache) :collect collect))
 8.12909-          ((:disable-cache)
 8.12910-           (process-output-translations-directive '(t t) :collect collect))
 8.12911-          ((:inherit-configuration)
 8.12912-           (inherit-output-translations inherit :collect collect))
 8.12913-          ((:ignore-inherited-configuration :ignore-invalid-entries nil)
 8.12914-           nil))
 8.12915-        (let ((src (first directive))
 8.12916-              (dst (second directive)))
 8.12917-          (if (eq src :include)
 8.12918-              (when dst
 8.12919-                (process-output-translations (pathname dst) :inherit nil :collect collect))
 8.12920-              (when src
 8.12921-                (let ((trusrc (or (eql src t)
 8.12922-                                  (let ((loc (resolve-location src :ensure-directory t :wilden t)))
 8.12923-                                    (if (absolute-pathname-p loc) (resolve-symlinks* loc) loc)))))
 8.12924-                  (cond
 8.12925-                    ((location-function-p dst)
 8.12926-                     (funcall collect
 8.12927-                              (list trusrc (ensure-function (second dst)))))
 8.12928-                    ((typep dst 'boolean)
 8.12929-                     (funcall collect (list trusrc t)))
 8.12930-                    (t
 8.12931-                     (let* ((trudst (resolve-location dst :ensure-directory t :wilden t)))
 8.12932-                       (funcall collect (list trudst t))
 8.12933-                       (funcall collect (list trusrc trudst)))))))))))
 8.12934-
 8.12935-  (defmethod process-output-translations ((x symbol) &key
 8.12936-                                                       (inherit *default-output-translations*)
 8.12937-                                                       collect)
 8.12938-    (process-output-translations (funcall x) :inherit inherit :collect collect))
 8.12939-  (defmethod process-output-translations ((pathname pathname) &key inherit collect)
 8.12940-    (cond
 8.12941-      ((directory-pathname-p pathname)
 8.12942-       (process-output-translations (validate-output-translations-directory pathname)
 8.12943-                                    :inherit inherit :collect collect))
 8.12944-      ((probe-file* pathname :truename *resolve-symlinks*)
 8.12945-       (process-output-translations (validate-output-translations-file pathname)
 8.12946-                                    :inherit inherit :collect collect))
 8.12947-      (t
 8.12948-       (inherit-output-translations inherit :collect collect))))
 8.12949-  (defmethod process-output-translations ((string string) &key inherit collect)
 8.12950-    (process-output-translations (parse-output-translations-string string)
 8.12951-                                 :inherit inherit :collect collect))
 8.12952-  (defmethod process-output-translations ((x null) &key inherit collect)
 8.12953-    (inherit-output-translations inherit :collect collect))
 8.12954-  (defmethod process-output-translations ((form cons) &key inherit collect)
 8.12955-    (dolist (directive (cdr (validate-output-translations-form form)))
 8.12956-      (process-output-translations-directive directive :inherit inherit :collect collect)))
 8.12957-
 8.12958-
 8.12959-  ;;; Top-level entry-points to configure output-translations
 8.12960-
 8.12961-  (defun compute-output-translations (&optional parameter)
 8.12962-    "read the configuration, return it"
 8.12963-    (remove-duplicates
 8.12964-     (while-collecting (c)
 8.12965-       (inherit-output-translations
 8.12966-        `(wrapping-output-translations ,parameter ,@*default-output-translations*) :collect #'c))
 8.12967-     :test 'equal :from-end t))
 8.12968-
 8.12969-  ;; Saving the user-provided parameter to output-translations, if any,
 8.12970-  ;; so we can recompute the translations after code upgrade.
 8.12971-  (defvar *output-translations-parameter* nil)
 8.12972-
 8.12973-  ;; Main entry-point for users.
 8.12974-  (defun initialize-output-translations (&optional (parameter *output-translations-parameter*))
 8.12975-    "read the configuration, initialize the internal configuration variable,
 8.12976-return the configuration"
 8.12977-    (setf *output-translations-parameter* parameter
 8.12978-          (output-translations) (compute-output-translations parameter)))
 8.12979-
 8.12980-  (defun disable-output-translations ()
 8.12981-    "Initialize output translations in a way that maps every file to itself,
 8.12982-effectively disabling the output translation facility."
 8.12983-    (initialize-output-translations
 8.12984-     '(:output-translations :disable-cache :ignore-inherited-configuration)))
 8.12985-
 8.12986-  ;; checks an initial variable to see whether the state is initialized
 8.12987-  ;; or cleared. In the former case, return current configuration; in
 8.12988-  ;; the latter, initialize.  ASDF will call this function at the start
 8.12989-  ;; of (asdf:find-system).
 8.12990-  (defun ensure-output-translations ()
 8.12991-    (if (output-translations-initialized-p)
 8.12992-        (output-translations)
 8.12993-        (initialize-output-translations)))
 8.12994-
 8.12995-
 8.12996-  ;; Top-level entry-point to _use_ output-translations
 8.12997-  (defun apply-output-translations (path)
 8.12998-    (etypecase path
 8.12999-      (logical-pathname
 8.13000-       path)
 8.13001-      ((or pathname string)
 8.13002-       (ensure-output-translations)
 8.13003-       (loop :with p = (resolve-symlinks* path)
 8.13004-             :for (source destination) :in (car *output-translations*)
 8.13005-             :for root = (when (or (eq source t)
 8.13006-                                   (and (pathnamep source)
 8.13007-                                        (not (absolute-pathname-p source))))
 8.13008-                           (pathname-root p))
 8.13009-             :for absolute-source = (cond
 8.13010-                                      ((eq source t) (wilden root))
 8.13011-                                      (root (merge-pathnames* source root))
 8.13012-                                      (t source))
 8.13013-             :when (or (eq source t) (pathname-match-p p absolute-source))
 8.13014-               :return (translate-pathname* p absolute-source destination root source)
 8.13015-             :finally (return p)))))
 8.13016-
 8.13017-
 8.13018-  ;; Hook into uiop's output-translation mechanism
 8.13019-  #-cormanlisp
 8.13020-  (setf *output-translation-function* 'apply-output-translations)
 8.13021-
 8.13022-
 8.13023-  ;;; Implementation-dependent hacks
 8.13024-  #+abcl ;; ABCL: make it possible to use systems provided in the ABCL jar.
 8.13025-  (defun translate-jar-pathname (source wildcard)
 8.13026-    (declare (ignore wildcard))
 8.13027-    (flet ((normalize-device (pathname)
 8.13028-             (if (find :windows *features*)
 8.13029-                 pathname
 8.13030-                 (make-pathname :defaults pathname :device :unspecific))))
 8.13031-      (let* ((jar
 8.13032-               (pathname (first (pathname-device source))))
 8.13033-             (target-root-directory-namestring
 8.13034-               (format nil "/___jar___file___root___/~@[~A/~]"
 8.13035-                       (and (find :windows *features*)
 8.13036-                            (pathname-device jar))))
 8.13037-             (relative-source
 8.13038-               (relativize-pathname-directory source))
 8.13039-             (relative-jar
 8.13040-               (relativize-pathname-directory (ensure-directory-pathname jar)))
 8.13041-             (target-root-directory
 8.13042-               (normalize-device
 8.13043-                (pathname-directory-pathname
 8.13044-                 (parse-namestring target-root-directory-namestring))))
 8.13045-             (target-root
 8.13046-               (merge-pathnames* relative-jar target-root-directory))
 8.13047-             (target
 8.13048-               (merge-pathnames* relative-source target-root)))
 8.13049-        (normalize-device (apply-output-translations target))))))
 8.13050-
 8.13051-;;;; -----------------------------------------------------------------
 8.13052-;;;; Source Registry Configuration, by Francois-Rene Rideau
 8.13053-;;;; See the Manual and https://bugs.launchpad.net/asdf/+bug/485918
 8.13054-
 8.13055-(uiop/package:define-package :asdf/source-registry
 8.13056-  ;; NB: asdf/find-system allows upgrade from <=3.2.1 that have initialize-source-registry there
 8.13057-  (:recycle :asdf/source-registry :asdf/find-system :asdf)
 8.13058-  (:use :uiop/common-lisp :uiop :asdf/upgrade :asdf/system :asdf/system-registry)
 8.13059-  (:export
 8.13060-   #:*source-registry-parameter* #:*default-source-registries*
 8.13061-   #:invalid-source-registry
 8.13062-   #:source-registry-initialized-p
 8.13063-   #:initialize-source-registry #:clear-source-registry #:*source-registry*
 8.13064-   #:ensure-source-registry #:*source-registry-parameter*
 8.13065-   #:*default-source-registry-exclusions* #:*source-registry-exclusions*
 8.13066-   #:*wild-asd* #:directory-asd-files #:register-asd-directory
 8.13067-   #:*recurse-beyond-asds* #:collect-asds-in-directory #:collect-sub*directories-asd-files
 8.13068-   #:validate-source-registry-directive #:validate-source-registry-form
 8.13069-   #:validate-source-registry-file #:validate-source-registry-directory
 8.13070-   #:parse-source-registry-string #:wrapping-source-registry
 8.13071-   #:default-user-source-registry #:default-system-source-registry
 8.13072-   #:user-source-registry #:system-source-registry
 8.13073-   #:user-source-registry-directory #:system-source-registry-directory
 8.13074-   #:environment-source-registry #:process-source-registry #:inherit-source-registry
 8.13075-   #:compute-source-registry #:flatten-source-registry
 8.13076-   #:sysdef-source-registry-search))
 8.13077-(in-package :asdf/source-registry)
 8.13078-
 8.13079-(with-upgradability ()
 8.13080-  (define-condition invalid-source-registry (invalid-configuration warning)
 8.13081-    ((format :initform (compatfmt "~@<Invalid source registry ~S~@[ in ~S~]~@{ ~@?~}~@:>"))))
 8.13082-
 8.13083-  ;; Default list of directories under which the source-registry tree search won't recurse
 8.13084-  (defvar *default-source-registry-exclusions*
 8.13085-    '(;;-- Using ack 1.2 exclusions
 8.13086-      ".bzr" ".cdv"
 8.13087-      ;; "~.dep" "~.dot" "~.nib" "~.plst" ; we don't support ack wildcards
 8.13088-      ".git" ".hg" ".pc" ".svn" "CVS" "RCS" "SCCS" "_darcs"
 8.13089-      "_sgbak" "autom4te.cache" "cover_db" "_build"
 8.13090-      ;;-- debian often builds stuff under the debian directory... BAD.
 8.13091-      "debian"))
 8.13092-
 8.13093-  ;; Actual list of directories under which the source-registry tree search won't recurse
 8.13094-  (defvar *source-registry-exclusions* *default-source-registry-exclusions*)
 8.13095-
 8.13096-  ;; The state of the source-registry after search in configured locations
 8.13097-  (defvar *source-registry* nil
 8.13098-    "Either NIL (for uninitialized), or an equal hash-table, mapping
 8.13099-system names to pathnames of .asd files")
 8.13100-
 8.13101-  ;; Saving the user-provided parameter to the source-registry, if any,
 8.13102-  ;; so we can recompute the source-registry after code upgrade.
 8.13103-  (defvar *source-registry-parameter* nil)
 8.13104-
 8.13105-  (defun source-registry-initialized-p ()
 8.13106-    (typep *source-registry* 'hash-table))
 8.13107-
 8.13108-  (defun clear-source-registry ()
 8.13109-    "Undoes any initialization of the source registry."
 8.13110-    (setf *source-registry* nil)
 8.13111-    (values))
 8.13112-  (register-clear-configuration-hook 'clear-source-registry)
 8.13113-
 8.13114-  (defparameter *wild-asd*
 8.13115-    (make-pathname :directory nil :name *wild* :type "asd" :version :newest))
 8.13116-
 8.13117-  (defun directory-asd-files (directory)
 8.13118-    (directory-files directory *wild-asd*))
 8.13119-
 8.13120-  (defun collect-asds-in-directory (directory collect)
 8.13121-    (let ((asds (directory-asd-files directory)))
 8.13122-      (map () collect asds)
 8.13123-      asds))
 8.13124-
 8.13125-  (defvar *recurse-beyond-asds* t
 8.13126-    "Should :tree entries of the source-registry recurse in subdirectories
 8.13127-after having found a .asd file? True by default.")
 8.13128-
 8.13129-  ;; When walking down a filesystem tree, if in a directory there is a .cl-source-registry.cache,
 8.13130-  ;; read its contents instead of further recursively querying the filesystem.
 8.13131-  (defun process-source-registry-cache (directory collect)
 8.13132-    (let ((cache (ignore-errors
 8.13133-                  (safe-read-file-form (subpathname directory ".cl-source-registry.cache")))))
 8.13134-      (when (and (listp cache) (eq :source-registry-cache (first cache)))
 8.13135-        (loop :for s :in (rest cache) :do (funcall collect (subpathname directory s)))
 8.13136-        t)))
 8.13137-
 8.13138-  (defun collect-sub*directories-asd-files
 8.13139-      (directory &key (exclude *default-source-registry-exclusions*) collect
 8.13140-                   (recurse-beyond-asds *recurse-beyond-asds*) ignore-cache)
 8.13141-    (let ((visited (make-hash-table :test 'equalp)))
 8.13142-      (flet ((collectp (dir)
 8.13143-               (unless (and (not ignore-cache) (process-source-registry-cache dir collect))
 8.13144-                 (let ((asds (collect-asds-in-directory dir collect)))
 8.13145-                   (or recurse-beyond-asds (not asds)))))
 8.13146-             (recursep (x)                    ; x will be a directory pathname
 8.13147-               (and
 8.13148-                (not (member (car (last (pathname-directory x))) exclude :test #'equal))
 8.13149-                (flet ((pathname-key (x)
 8.13150-                         (namestring (truename* x))))
 8.13151-                  (let ((visitedp (gethash (pathname-key x) visited)))
 8.13152-                    (if visitedp nil
 8.13153-                        (setf (gethash (pathname-key x) visited) t)))))))
 8.13154-      (collect-sub*directories directory #'collectp #'recursep (constantly nil)))))
 8.13155-
 8.13156-
 8.13157-  ;;; Validate the configuration forms
 8.13158-
 8.13159-  (defun validate-source-registry-directive (directive)
 8.13160-    (or (member directive '(:default-registry))
 8.13161-        (and (consp directive)
 8.13162-             (let ((rest (rest directive)))
 8.13163-               (case (first directive)
 8.13164-                 ((:include :directory :tree)
 8.13165-                  (and (length=n-p rest 1)
 8.13166-                       (location-designator-p (first rest))))
 8.13167-                 ((:exclude :also-exclude)
 8.13168-                  (every #'stringp rest))
 8.13169-                 ((:default-registry)
 8.13170-                  (null rest)))))))
 8.13171-
 8.13172-  (defun validate-source-registry-form (form &key location)
 8.13173-    (validate-configuration-form
 8.13174-     form :source-registry 'validate-source-registry-directive
 8.13175-          :location location :invalid-form-reporter 'invalid-source-registry))
 8.13176-
 8.13177-  (defun validate-source-registry-file (file)
 8.13178-    (validate-configuration-file
 8.13179-     file 'validate-source-registry-form :description "a source registry"))
 8.13180-
 8.13181-  (defun validate-source-registry-directory (directory)
 8.13182-    (validate-configuration-directory
 8.13183-     directory :source-registry 'validate-source-registry-directive
 8.13184-               :invalid-form-reporter 'invalid-source-registry))
 8.13185-
 8.13186-
 8.13187-  ;;; Parse the configuration string
 8.13188-
 8.13189-  (defun parse-source-registry-string (string &key location)
 8.13190-    (cond
 8.13191-      ((or (null string) (equal string ""))
 8.13192-       '(:source-registry :inherit-configuration))
 8.13193-      ((not (stringp string))
 8.13194-       (error (compatfmt "~@<Environment string isn't: ~3i~_~S~@:>") string))
 8.13195-      ((find (char string 0) "\"(")
 8.13196-       (validate-source-registry-form (read-from-string string) :location location))
 8.13197-      (t
 8.13198-       (loop
 8.13199-         :with inherit = nil
 8.13200-         :with directives = ()
 8.13201-         :with start = 0
 8.13202-         :with end = (length string)
 8.13203-         :with separator = (inter-directory-separator)
 8.13204-         :for pos = (position separator string :start start) :do
 8.13205-           (let ((s (subseq string start (or pos end))))
 8.13206-             (flet ((check (dir)
 8.13207-                      (unless (absolute-pathname-p dir)
 8.13208-                        (error (compatfmt "~@<source-registry string must specify absolute pathnames: ~3i~_~S~@:>") string))
 8.13209-                      dir))
 8.13210-               (cond
 8.13211-                 ((equal "" s) ; empty element: inherit
 8.13212-                  (when inherit
 8.13213-                    (error (compatfmt "~@<Only one inherited configuration allowed: ~3i~_~S~@:>")
 8.13214-                           string))
 8.13215-                  (setf inherit t)
 8.13216-                  (push ':inherit-configuration directives))
 8.13217-                 ((string-suffix-p s "//") ;; TODO: allow for doubling of separator even outside Unix?
 8.13218-                  (push `(:tree ,(check (subseq s 0 (- (length s) 2)))) directives))
 8.13219-                 (t
 8.13220-                  (push `(:directory ,(check s)) directives))))
 8.13221-             (cond
 8.13222-               (pos
 8.13223-                (setf start (1+ pos)))
 8.13224-               (t
 8.13225-                (unless inherit
 8.13226-                  (push '(:ignore-inherited-configuration) directives))
 8.13227-                (return `(:source-registry ,@(nreverse directives))))))))))
 8.13228-
 8.13229-  (defun register-asd-directory (directory &key recurse exclude collect)
 8.13230-    (if (not recurse)
 8.13231-        (collect-asds-in-directory directory collect)
 8.13232-        (collect-sub*directories-asd-files
 8.13233-         directory :exclude exclude :collect collect)))
 8.13234-
 8.13235-  (defparameter* *default-source-registries*
 8.13236-    '(environment-source-registry
 8.13237-      user-source-registry
 8.13238-      user-source-registry-directory
 8.13239-      default-user-source-registry
 8.13240-      system-source-registry
 8.13241-      system-source-registry-directory
 8.13242-      default-system-source-registry)
 8.13243-    "List of default source registries" "3.1.0.102")
 8.13244-
 8.13245-  (defparameter *source-registry-file* (parse-unix-namestring "common-lisp/source-registry.conf"))
 8.13246-  (defparameter *source-registry-directory* (parse-unix-namestring "common-lisp/source-registry.conf.d/"))
 8.13247-
 8.13248-  (defun wrapping-source-registry ()
 8.13249-    `(:source-registry
 8.13250-      #+(or clasp ecl sbcl) (:tree ,(resolve-symlinks* (lisp-implementation-directory)))
 8.13251-      :inherit-configuration
 8.13252-      #+mkcl (:tree ,(translate-logical-pathname "SYS:"))
 8.13253-      #+cmucl (:tree #p"modules:")
 8.13254-      #+scl (:tree #p"file://modules/")))
 8.13255-  (defun default-user-source-registry ()
 8.13256-    `(:source-registry
 8.13257-      (:tree (:home "common-lisp/"))
 8.13258-      #+sbcl (:directory (:home ".sbcl/systems/"))
 8.13259-      (:directory ,(xdg-data-home "common-lisp/systems/"))
 8.13260-      (:tree ,(xdg-data-home "common-lisp/source/"))
 8.13261-      :inherit-configuration))
 8.13262-  (defun default-system-source-registry ()
 8.13263-    `(:source-registry
 8.13264-      ,@(loop :for dir :in (xdg-data-dirs "common-lisp/")
 8.13265-              :collect `(:directory (,dir "systems/"))
 8.13266-              :collect `(:tree (,dir "source/")))
 8.13267-      :inherit-configuration))
 8.13268-  (defun user-source-registry (&key (direction :input))
 8.13269-    (xdg-config-pathname *source-registry-file* direction))
 8.13270-  (defun system-source-registry (&key (direction :input))
 8.13271-    (find-preferred-file (system-config-pathnames *source-registry-file*)
 8.13272-                         :direction direction))
 8.13273-  (defun user-source-registry-directory (&key (direction :input))
 8.13274-    (xdg-config-pathname *source-registry-directory* direction))
 8.13275-  (defun system-source-registry-directory (&key (direction :input))
 8.13276-    (find-preferred-file (system-config-pathnames *source-registry-directory*)
 8.13277-                         :direction direction))
 8.13278-  (defun environment-source-registry ()
 8.13279-    (getenv "CL_SOURCE_REGISTRY"))
 8.13280-
 8.13281-
 8.13282-  ;;; Process the source-registry configuration
 8.13283-
 8.13284-  (defgeneric process-source-registry (spec &key inherit register))
 8.13285-
 8.13286-  (defun inherit-source-registry (inherit &key register)
 8.13287-    (when inherit
 8.13288-      (process-source-registry (first inherit) :register register :inherit (rest inherit))))
 8.13289-
 8.13290-  (defun process-source-registry-directive (directive &key inherit register)
 8.13291-    (destructuring-bind (kw &rest rest) (if (consp directive) directive (list directive))
 8.13292-      (ecase kw
 8.13293-        ((:include)
 8.13294-         (destructuring-bind (pathname) rest
 8.13295-           (process-source-registry (resolve-location pathname) :inherit nil :register register)))
 8.13296-        ((:directory)
 8.13297-         (destructuring-bind (pathname) rest
 8.13298-           (when pathname
 8.13299-             (funcall register (resolve-location pathname :ensure-directory t)))))
 8.13300-        ((:tree)
 8.13301-         (destructuring-bind (pathname) rest
 8.13302-           (when pathname
 8.13303-             (funcall register (resolve-location pathname :ensure-directory t)
 8.13304-                      :recurse t :exclude *source-registry-exclusions*))))
 8.13305-        ((:exclude)
 8.13306-         (setf *source-registry-exclusions* rest))
 8.13307-        ((:also-exclude)
 8.13308-         (appendf *source-registry-exclusions* rest))
 8.13309-        ((:default-registry)
 8.13310-         (inherit-source-registry
 8.13311-          '(default-user-source-registry default-system-source-registry) :register register))
 8.13312-        ((:inherit-configuration)
 8.13313-         (inherit-source-registry inherit :register register))
 8.13314-        ((:ignore-inherited-configuration)
 8.13315-         nil)))
 8.13316-    nil)
 8.13317-
 8.13318-  (defmethod process-source-registry ((x symbol) &key inherit register)
 8.13319-    (process-source-registry (funcall x) :inherit inherit :register register))
 8.13320-  (defmethod process-source-registry ((pathname pathname) &key inherit register)
 8.13321-    (cond
 8.13322-      ((directory-pathname-p pathname)
 8.13323-       (let ((*here-directory* (resolve-symlinks* pathname)))
 8.13324-         (process-source-registry (validate-source-registry-directory pathname)
 8.13325-                                  :inherit inherit :register register)))
 8.13326-      ((probe-file* pathname :truename *resolve-symlinks*)
 8.13327-       (let ((*here-directory* (pathname-directory-pathname pathname)))
 8.13328-         (process-source-registry (validate-source-registry-file pathname)
 8.13329-                                  :inherit inherit :register register)))
 8.13330-      (t
 8.13331-       (inherit-source-registry inherit :register register))))
 8.13332-  (defmethod process-source-registry ((string string) &key inherit register)
 8.13333-    (process-source-registry (parse-source-registry-string string)
 8.13334-                             :inherit inherit :register register))
 8.13335-  (defmethod process-source-registry ((x null) &key inherit register)
 8.13336-    (inherit-source-registry inherit :register register))
 8.13337-  (defmethod process-source-registry ((form cons) &key inherit register)
 8.13338-    (let ((*source-registry-exclusions* *default-source-registry-exclusions*))
 8.13339-      (dolist (directive (cdr (validate-source-registry-form form)))
 8.13340-        (process-source-registry-directive directive :inherit inherit :register register))))
 8.13341-
 8.13342-
 8.13343-  ;; Flatten the user-provided configuration into an ordered list of directories and trees
 8.13344-  (defun flatten-source-registry (&optional (parameter *source-registry-parameter*))
 8.13345-    (remove-duplicates
 8.13346-     (while-collecting (collect)
 8.13347-       (with-pathname-defaults () ;; be location-independent
 8.13348-         (inherit-source-registry
 8.13349-          `(wrapping-source-registry
 8.13350-            ,parameter
 8.13351-            ,@*default-source-registries*)
 8.13352-          :register #'(lambda (directory &key recurse exclude)
 8.13353-                        (collect (list directory :recurse recurse :exclude exclude))))))
 8.13354-     :test 'equal :from-end t))
 8.13355-
 8.13356-  ;; MAYBE: move this utility function to uiop/pathname and export it?
 8.13357-  (defun pathname-directory-depth (p)
 8.13358-    (length (normalize-pathname-directory-component (pathname-directory p))))
 8.13359-
 8.13360-  (defun preferred-source-path-p (x y)
 8.13361-    "Return T iff X is to be preferred over Y as a source path"
 8.13362-    (let ((lx (pathname-directory-depth x))
 8.13363-          (ly (pathname-directory-depth y)))
 8.13364-      (or (< lx ly)
 8.13365-          (and (= lx ly)
 8.13366-               (string< (namestring x)
 8.13367-                        (namestring y))))))
 8.13368-
 8.13369-  ;; Will read the configuration and initialize all internal variables.
 8.13370-  (defun compute-source-registry (&optional (parameter *source-registry-parameter*)
 8.13371-                                    (registry *source-registry*))
 8.13372-    (dolist (entry (flatten-source-registry parameter))
 8.13373-      (destructuring-bind (directory &key recurse exclude) entry
 8.13374-        (let* ((h (make-hash-table :test 'equal))) ; table to detect duplicates
 8.13375-          (register-asd-directory
 8.13376-           directory :recurse recurse :exclude exclude :collect
 8.13377-           #'(lambda (asd)
 8.13378-               (let* ((name (pathname-name asd))
 8.13379-                      (name (if (typep asd 'logical-pathname)
 8.13380-                                ;; logical pathnames are upper-case,
 8.13381-                                ;; at least in the CLHS and on SBCL,
 8.13382-                                ;; yet (coerce-name :foo) is lower-case.
 8.13383-                                ;; won't work well with (load-system "Foo")
 8.13384-                                ;; instead of (load-system 'foo)
 8.13385-                                (string-downcase name)
 8.13386-                                name)))
 8.13387-                 (unless (gethash name registry) ; already shadowed by something else
 8.13388-                   (if-let (old (gethash name h))
 8.13389-                     ;; If the name appears multiple times,
 8.13390-                     ;; prefer the one with the shallowest directory,
 8.13391-                     ;; or if they have same depth, compare unix-namestring with string<
 8.13392-                     (multiple-value-bind (better worse)
 8.13393-                         (if (preferred-source-path-p asd old)
 8.13394-                             (progn (setf (gethash name h) asd) (values asd old))
 8.13395-                             (values old asd))
 8.13396-                       (when *verbose-out*
 8.13397-                         (warn (compatfmt "~@<In source-registry entry ~A~@[/~*~] ~
 8.13398-                                              found several entries for ~A - picking ~S over ~S~:>")
 8.13399-                               directory recurse name better worse)))
 8.13400-                     (setf (gethash name h) asd))))))
 8.13401-          (maphash #'(lambda (k v) (setf (gethash k registry) v)) h))))
 8.13402-    (values))
 8.13403-
 8.13404-  (defun initialize-source-registry (&optional (parameter *source-registry-parameter*))
 8.13405-    ;; Record the parameter used to configure the registry
 8.13406-    (setf *source-registry-parameter* parameter)
 8.13407-    ;; Clear the previous registry database:
 8.13408-    (setf *source-registry* (make-hash-table :test 'equal))
 8.13409-    ;; Do it!
 8.13410-    (compute-source-registry parameter))
 8.13411-
 8.13412-  ;; Checks an initial variable to see whether the state is initialized
 8.13413-  ;; or cleared. In the former case, return current configuration; in
 8.13414-  ;; the latter, initialize.  ASDF will call this function at the start
 8.13415-  ;; of (asdf:find-system) to make sure the source registry is initialized.
 8.13416-  ;; However, it will do so *without* a parameter, at which point it
 8.13417-  ;; will be too late to provide a parameter to this function, though
 8.13418-  ;; you may override the configuration explicitly by calling
 8.13419-  ;; initialize-source-registry directly with your parameter.
 8.13420-  (defun ensure-source-registry (&optional parameter)
 8.13421-    (unless (source-registry-initialized-p)
 8.13422-      (initialize-source-registry parameter))
 8.13423-    (values))
 8.13424-
 8.13425-  (defun sysdef-source-registry-search (system)
 8.13426-    (ensure-source-registry)
 8.13427-    (values (gethash (primary-system-name system) *source-registry*))))
 8.13428-
 8.13429-
 8.13430-;;;; -------------------------------------------------------------------------
 8.13431-;;; Internal hacks for backward-compatibility
 8.13432-
 8.13433-(uiop/package:define-package :asdf/backward-internals
 8.13434-  (:recycle :asdf/backward-internals :asdf)
 8.13435-  (:use :uiop/common-lisp :uiop :asdf/upgrade :asdf/find-system)
 8.13436-  (:export #:load-sysdef))
 8.13437-(in-package :asdf/backward-internals)
 8.13438-
 8.13439-(with-asdf-deprecation (:style-warning "3.2" :warning "3.4")
 8.13440-  (defun load-sysdef (name pathname)
 8.13441-    (declare (ignore name pathname))
 8.13442-    ;; Needed for backward compatibility with swank-asdf from SLIME 2015-12-01 or older.
 8.13443-    (error "Use asdf:load-asd instead of asdf::load-sysdef")))
 8.13444-;;;; -------------------------------------------------------------------------
 8.13445-;;; Backward-compatible interfaces
 8.13446-
 8.13447-(uiop/package:define-package :asdf/backward-interface
 8.13448-  (:recycle :asdf/backward-interface :asdf)
 8.13449-  (:use :uiop/common-lisp :uiop :asdf/upgrade :asdf/session
 8.13450-   :asdf/component :asdf/system :asdf/system-registry :asdf/operation :asdf/action
 8.13451-   :asdf/lisp-action :asdf/plan :asdf/operate
 8.13452-   :asdf/find-system :asdf/parse-defsystem :asdf/output-translations :asdf/bundle)
 8.13453-  (:export
 8.13454-   #:*asdf-verbose*
 8.13455-   #:operation-error #:compile-error #:compile-failed #:compile-warned
 8.13456-   #:error-component #:error-operation #:traverse
 8.13457-   #:component-load-dependencies
 8.13458-   #:enable-asdf-binary-locations-compatibility
 8.13459-   #:operation-on-failure #:operation-on-warnings #:on-failure #:on-warnings
 8.13460-   #:component-property
 8.13461-   #:run-shell-command
 8.13462-   #:system-definition-pathname #:system-registered-p #:require-system
 8.13463-   #:explain
 8.13464-   #+ecl #:make-build))
 8.13465-(in-package :asdf/backward-interface)
 8.13466-
 8.13467-;; NB: the warning status of these functions may have to be distinguished later,
 8.13468-;; as some get removed faster than the others in client code.
 8.13469-(with-asdf-deprecation (:style-warning "3.2" :warning "3.4")
 8.13470-
 8.13471-  ;; These conditions from ASDF 1 and 2 are used by many packages in Quicklisp;
 8.13472-  ;; but ASDF3 replaced them with somewhat different variants of uiop:compile-condition
 8.13473-  ;; that do not involve ASDF actions.
 8.13474-  ;; TODO: find the offenders and stop them.
 8.13475-  (progn
 8.13476-    (define-condition operation-error (error) ;; Bad, backward-compatible name
 8.13477-      ;; Used by SBCL, cffi-tests, clsql-mysql, clsql-uffi, qt, elephant, uffi-tests, sb-grovel
 8.13478-      ((component :reader error-component :initarg :component)
 8.13479-       (operation :reader error-operation :initarg :operation))
 8.13480-      (:report (lambda (c s)
 8.13481-                 (format s (compatfmt "~@<~A while invoking ~A on ~A~@:>")
 8.13482-                         (type-of c) (error-operation c) (error-component c)))))
 8.13483-    (define-condition compile-error (operation-error) ())
 8.13484-    (define-condition compile-failed (compile-error) ())
 8.13485-    (define-condition compile-warned (compile-error) ()))
 8.13486-
 8.13487-  ;; In Quicklisp 2015-05, still used by lisp-executable, staple, repl-utilities, cffi
 8.13488-  (defun component-load-dependencies (component) ;; from ASDF 2.000 to 2.26
 8.13489-    "DEPRECATED. Please use COMPONENT-SIDEWAY-DEPENDENCIES instead; or better,
 8.13490-define your operations with proper use of SIDEWAY-OPERATION, SELFWARD-OPERATION,
 8.13491-or define methods on PREPARE-OP, etc."
 8.13492-    ;; Old deprecated name for the same thing. Please update your software.
 8.13493-    (component-sideway-dependencies component))
 8.13494-
 8.13495-  ;; These old interfaces from ASDF1 have never been very meaningful
 8.13496-  ;; but are still used in obscure places.
 8.13497-  ;; In Quicklisp 2015-05, still used by cl-protobufs and clx.
 8.13498-  (defgeneric operation-on-warnings (operation)
 8.13499-    (:documentation "DEPRECATED. Please use UIOP:*COMPILE-FILE-WARNINGS-BEHAVIOUR* instead."))
 8.13500-  (defgeneric operation-on-failure (operation)
 8.13501-    (:documentation "DEPRECATED. Please use UIOP:*COMPILE-FILE-FAILURE-BEHAVIOUR* instead."))
 8.13502-  (defgeneric (setf operation-on-warnings) (x operation)
 8.13503-    (:documentation "DEPRECATED. Please SETF UIOP:*COMPILE-FILE-WARNINGS-BEHAVIOUR* instead."))
 8.13504-  (defgeneric (setf operation-on-failure) (x operation)
 8.13505-    (:documentation "DEPRECATED. Please SETF UIOP:*COMPILE-FILE-FAILURE-BEHAVIOUR* instead."))
 8.13506-  (progn
 8.13507-    (defmethod operation-on-warnings ((o operation))
 8.13508-      *compile-file-warnings-behaviour*)
 8.13509-    (defmethod operation-on-failure ((o operation))
 8.13510-      *compile-file-failure-behaviour*)
 8.13511-    (defmethod (setf operation-on-warnings) (x (o operation))
 8.13512-      (setf *compile-file-warnings-behaviour* x))
 8.13513-    (defmethod (setf operation-on-failure) (x (o operation))
 8.13514-      (setf *compile-file-failure-behaviour* x)))
 8.13515-
 8.13516-  ;; Quicklisp 2015-05: Still used by SLIME's swank-asdf (!), common-lisp-stat,
 8.13517-  ;; js-parser, osicat, babel, staple, weblocks, cl-png, plain-odbc, autoproject,
 8.13518-  ;; cl-blapack, com.informatimago, cells-gtk3, asdf-dependency-grovel,
 8.13519-  ;; cl-glfw, cffi, jwacs, montezuma
 8.13520-  (defun system-definition-pathname (x)
 8.13521-    ;; As of 2.014.8, we mean to make this function obsolete,
 8.13522-    ;; but that won't happen until all clients have been updated.
 8.13523-    "DEPRECATED. This function used to expose ASDF internals with subtle
 8.13524-differences with respect to user expectations, that have been refactored
 8.13525-away since. We recommend you use ASDF:SYSTEM-SOURCE-FILE instead for a
 8.13526-mostly compatible replacement that we're supporting, or even
 8.13527-ASDF:SYSTEM-SOURCE-DIRECTORY or ASDF:SYSTEM-RELATIVE-PATHNAME
 8.13528-if that's whay you mean." ;;)
 8.13529-    (system-source-file x))
 8.13530-
 8.13531-  ;; TRAVERSE is the function used to compute a plan in ASDF 1 and 2.
 8.13532-  ;; It was never officially exposed but some people still used it.
 8.13533-  (defgeneric traverse (operation component &key &allow-other-keys)
 8.13534-    (:documentation
 8.13535-     "DEPRECATED. Use MAKE-PLAN and PLAN-ACTIONS, or REQUIRED-COMPONENTS,
 8.13536-or some other supported interface instead.
 8.13537-
 8.13538-Generate and return a plan for performing OPERATION on COMPONENT.
 8.13539-
 8.13540-The plan returned is a list of dotted-pairs. Each pair is the CONS
 8.13541-of ASDF operation object and a COMPONENT object. The pairs will be
 8.13542-processed in order by OPERATE."))
 8.13543-  (progn
 8.13544-    (define-convenience-action-methods traverse (operation component &key)))
 8.13545-  (defmethod traverse ((o operation) (c component) &rest keys &key plan-class &allow-other-keys)
 8.13546-    (plan-actions (apply 'make-plan plan-class o c keys)))
 8.13547-
 8.13548-
 8.13549-  ;; ASDF-Binary-Locations compatibility
 8.13550-  ;; This remains supported for legacy user, but not recommended for new users.
 8.13551-  ;; We suspect there are no more legacy users in 2016.
 8.13552-  (defun enable-asdf-binary-locations-compatibility
 8.13553-      (&key
 8.13554-         (centralize-lisp-binaries nil)
 8.13555-         (default-toplevel-directory
 8.13556-             ;; Use ".cache/common-lisp/" instead ???
 8.13557-             (subpathname (user-homedir-pathname) ".fasls/"))
 8.13558-         (include-per-user-information nil)
 8.13559-         (map-all-source-files (or #+(or clasp clisp ecl mkcl) t nil))
 8.13560-         (source-to-target-mappings nil)
 8.13561-         (file-types `(,(compile-file-type)
 8.13562-                        "build-report"
 8.13563-                        #+clasp (compile-file-type :output-type :object)
 8.13564-                        #+ecl (compile-file-type :type :object)
 8.13565-                        #+mkcl (compile-file-type :fasl-p nil)
 8.13566-                        #+clisp "lib" #+sbcl "cfasl"
 8.13567-                        #+sbcl "sbcl-warnings" #+clozure "ccl-warnings")))
 8.13568-    "DEPRECATED. Use asdf-output-translations instead."
 8.13569-    #+(or clasp clisp ecl mkcl)
 8.13570-    (when (null map-all-source-files)
 8.13571-      (error "asdf:enable-asdf-binary-locations-compatibility doesn't support :map-all-source-files nil on CLISP, ECL and MKCL"))
 8.13572-    (let* ((patterns (if map-all-source-files (list *wild-file*)
 8.13573-                         (loop :for type :in file-types
 8.13574-                           :collect (make-pathname :type type :defaults *wild-file*))))
 8.13575-           (destination-directory
 8.13576-            (if centralize-lisp-binaries
 8.13577-                `(,default-toplevel-directory
 8.13578-                     ,@(when include-per-user-information
 8.13579-                             (cdr (pathname-directory (user-homedir-pathname))))
 8.13580-                     :implementation ,*wild-inferiors*)
 8.13581-                `(:root ,*wild-inferiors* :implementation))))
 8.13582-      (initialize-output-translations
 8.13583-       `(:output-translations
 8.13584-         ,@source-to-target-mappings
 8.13585-         #+abcl (#p"jar:file:/**/*.jar!/**/*.*" (:function translate-jar-pathname))
 8.13586-         #+abcl (#p"/___jar___file___root___/**/*.*" (,@destination-directory))
 8.13587-         ,@(loop :for pattern :in patterns
 8.13588-             :collect `((:root ,*wild-inferiors* ,pattern)
 8.13589-                        (,@destination-directory ,pattern)))
 8.13590-         (t t)
 8.13591-         :ignore-inherited-configuration))))
 8.13592-  (progn
 8.13593-    (defmethod operate :before (operation-class system &rest args &key &allow-other-keys)
 8.13594-      (declare (ignore operation-class system args))
 8.13595-      (when (find-symbol* '#:output-files-for-system-and-operation :asdf nil)
 8.13596-        (error "ASDF 2 is not compatible with ASDF-BINARY-LOCATIONS, which you are using.
 8.13597-ASDF 2 now achieves the same purpose with its builtin ASDF-OUTPUT-TRANSLATIONS,
 8.13598-which should be easier to configure. Please stop using ASDF-BINARY-LOCATIONS,
 8.13599-and instead use ASDF-OUTPUT-TRANSLATIONS. See the ASDF manual for details.
 8.13600-In case you insist on preserving your previous A-B-L configuration, but
 8.13601-do not know how to achieve the same effect with A-O-T, you may use function
 8.13602-ASDF:ENABLE-ASDF-BINARY-LOCATIONS-COMPATIBILITY as documented in the manual;
 8.13603-call that function where you would otherwise have loaded and configured A-B-L."))))
 8.13604-
 8.13605-
 8.13606-  ;; run-shell-command from ASDF 2, lightly fixed from ASDF 1, copied from MK-DEFSYSTEM. Die!
 8.13607-  (defun run-shell-command (control-string &rest args)
 8.13608-    "PLEASE DO NOT USE. This function is not just DEPRECATED, but also dysfunctional.
 8.13609-Please use UIOP:RUN-PROGRAM instead."
 8.13610-    #-(and ecl os-windows)
 8.13611-    (let ((command (apply 'format nil control-string args)))
 8.13612-      (asdf-message "; $ ~A~%" command)
 8.13613-      (let ((exit-code
 8.13614-             (ignore-errors
 8.13615-               (nth-value 2 (run-program command :force-shell t :ignore-error-status t
 8.13616-                                         :output *verbose-out*)))))
 8.13617-        (typecase exit-code
 8.13618-          ((integer 0 255) exit-code)
 8.13619-          (t 255))))
 8.13620-    #+(and ecl os-windows)
 8.13621-    (not-implemented-error "run-shell-command" "for ECL on Windows."))
 8.13622-
 8.13623-  ;; HOW do we get rid of variables??? With a symbol-macro that issues a warning?
 8.13624-  ;; In Quicklisp 2015-05, cl-protobufs still uses it, but that should be fixed in next version.
 8.13625-  (progn
 8.13626-    (defvar *asdf-verbose* nil)) ;; backward-compatibility with ASDF2 only. Unused.
 8.13627-
 8.13628-  ;; Do NOT use in new code. NOT SUPPORTED.
 8.13629-  ;; NB: When this goes away, remove the slot PROPERTY in COMPONENT.
 8.13630-  ;; In Quicklisp 2014-05, it's still used by yaclml, amazon-ecs, blackthorn-engine, cl-tidy.
 8.13631-  ;; See TODO for further cleanups required before to get rid of it.
 8.13632-  (defgeneric component-property (component property))
 8.13633-  (defgeneric (setf component-property) (new-value component property))
 8.13634-
 8.13635-  (defmethod component-property ((c component) property)
 8.13636-    (cdr (assoc property (slot-value c 'properties) :test #'equal)))
 8.13637-
 8.13638-  (defmethod (setf component-property) (new-value (c component) property)
 8.13639-    (let ((a (assoc property (slot-value c 'properties) :test #'equal)))
 8.13640-      (if a
 8.13641-          (setf (cdr a) new-value)
 8.13642-          (setf (slot-value c 'properties)
 8.13643-                (acons property new-value (slot-value c 'properties)))))
 8.13644-    new-value)
 8.13645-
 8.13646-
 8.13647-  ;; This method survives from ASDF 1, but really it is superseded by action-description.
 8.13648-  (defgeneric explain (operation component)
 8.13649-    (:documentation "Display a message describing an action.
 8.13650-
 8.13651-DEPRECATED. Use ASDF:ACTION-DESCRIPTION and/or ASDF::FORMAT-ACTION instead."))
 8.13652-  (progn
 8.13653-    (define-convenience-action-methods explain (operation component)))
 8.13654-  (defmethod explain ((o operation) (c component))
 8.13655-    (asdf-message (compatfmt "~&~@<; ~@;~A~:>~%") (action-description o c))))
 8.13656-
 8.13657-(with-asdf-deprecation (:style-warning "3.3")
 8.13658-  (defun system-registered-p (name)
 8.13659-    "DEPRECATED. Return a generalized boolean that is true if a system of given NAME was registered already.
 8.13660-NAME is a system designator, to be normalized by COERCE-NAME.
 8.13661-The value returned if true is a pair of a timestamp and a system object."
 8.13662-    (if-let (system (registered-system name))
 8.13663-      (cons (if-let (primary-system (registered-system (primary-system-name name)))
 8.13664-              (component-operation-time 'define-op primary-system))
 8.13665-            system)))
 8.13666-
 8.13667-  (defun require-system (system &rest keys &key &allow-other-keys)
 8.13668-    "Ensure the specified SYSTEM is loaded, passing the KEYS to OPERATE, but do not update the
 8.13669-system or its dependencies if it has already been loaded."
 8.13670-    (declare (ignore keys))
 8.13671-    (unless (component-loaded-p system)
 8.13672-      (load-system system))))
 8.13673-
 8.13674-;;; This function is for backward compatibility with ECL only.
 8.13675-#+ecl
 8.13676-(with-asdf-deprecation (:style-warning "3.2" :warning "9999")
 8.13677-  (defun make-build (system &rest args
 8.13678-                     &key (monolithic nil) (type :fasl) (move-here nil move-here-p)
 8.13679-                       prologue-code epilogue-code no-uiop
 8.13680-                       prefix-lisp-object-files postfix-lisp-object-files extra-object-files
 8.13681-                       &allow-other-keys)
 8.13682-    (let* ((operation (asdf/bundle::select-bundle-operation type monolithic))
 8.13683-           (move-here-path (if (and move-here
 8.13684-                                    (typep move-here '(or pathname string)))
 8.13685-                               (ensure-pathname move-here :namestring :lisp :ensure-directory t)
 8.13686-                               (system-relative-pathname system "asdf-output/")))
 8.13687-           (extra-build-args (remove-plist-keys
 8.13688-                              '(:monolithic :type :move-here
 8.13689-                                :prologue-code :epilogue-code :no-uiop
 8.13690-                                :prefix-lisp-object-files :postfix-lisp-object-files
 8.13691-                                :extra-object-files)
 8.13692-                              args))
 8.13693-           (build-system (if (subtypep operation 'image-op)
 8.13694-                             (eval `(defsystem "asdf.make-build"
 8.13695-                                      :class program-system
 8.13696-                                      :source-file nil
 8.13697-                                      :pathname ,(system-source-directory system)
 8.13698-                                      :build-operation ,operation
 8.13699-                                      :build-pathname ,(subpathname move-here-path
 8.13700-                                                                    (file-namestring (first (output-files operation system))))
 8.13701-                                      :depends-on (,(coerce-name system))
 8.13702-                                      :prologue-code ,prologue-code
 8.13703-                                      :epilogue-code ,epilogue-code
 8.13704-                                      :no-uiop ,no-uiop
 8.13705-                                      :prefix-lisp-object-files ,prefix-lisp-object-files
 8.13706-                                      :postfix-lisp-object-files ,postfix-lisp-object-files
 8.13707-                                      :extra-object-files ,extra-object-files
 8.13708-                                      :extra-build-args ,extra-build-args))
 8.13709-                             system))
 8.13710-           (files (output-files operation build-system)))
 8.13711-      (operate operation build-system)
 8.13712-      (if (or move-here
 8.13713-              (and (null move-here-p) (member operation '(program-op image-op))))
 8.13714-          (loop :with dest-path = (resolve-symlinks* (ensure-directories-exist move-here-path))
 8.13715-            :for f :in files
 8.13716-            :for new-f = (make-pathname :name (pathname-name f)
 8.13717-                                        :type (pathname-type f)
 8.13718-                                        :defaults dest-path)
 8.13719-            :do (rename-file-overwriting-target f new-f)
 8.13720-            :collect new-f)
 8.13721-          files))))
 8.13722-;;;; ---------------------------------------------------------------------------
 8.13723-;;;; Handle ASDF package upgrade, including implementation-dependent magic.
 8.13724-
 8.13725-(uiop/package:define-package :asdf/interface
 8.13726-  (:nicknames :asdf :asdf-utilities)
 8.13727-  (:recycle :asdf/interface :asdf)
 8.13728-  (:unintern
 8.13729-   #:loaded-systems ; makes for annoying SLIME completion
 8.13730-   #:output-files-for-system-and-operation) ; ASDF-BINARY-LOCATION function we use to detect ABL
 8.13731-  (:use :uiop/common-lisp :uiop :asdf/upgrade :asdf/session
 8.13732-   :asdf/component :asdf/system :asdf/system-registry :asdf/find-component
 8.13733-   :asdf/operation :asdf/action :asdf/lisp-action
 8.13734-   :asdf/output-translations :asdf/source-registry
 8.13735-   :asdf/forcing :asdf/plan :asdf/operate :asdf/find-system :asdf/parse-defsystem
 8.13736-   :asdf/bundle :asdf/concatenate-source
 8.13737-   :asdf/backward-internals :asdf/backward-interface :asdf/package-inferred-system)
 8.13738-  ;; Note: (1) we are NOT automatically reexporting everything from previous packages.
 8.13739-  ;; (2) we only reexport UIOP functionality when backward-compatibility requires it.
 8.13740-  (:export
 8.13741-   #:defsystem #:find-system #:load-asd #:locate-system #:coerce-name
 8.13742-   #:primary-system-name #:primary-system-p
 8.13743-   #:oos #:operate #:make-plan #:perform-plan #:sequential-plan
 8.13744-   #:system-definition-pathname
 8.13745-   #:search-for-system-definition #:find-component #:component-find-path
 8.13746-   #:compile-system #:load-system #:load-systems #:load-systems*
 8.13747-   #:require-system #:test-system #:clear-system
 8.13748-   #:operation #:make-operation #:find-operation
 8.13749-   #:upward-operation #:downward-operation #:sideway-operation #:selfward-operation
 8.13750-                      #:non-propagating-operation
 8.13751-   #:build-op #:make
 8.13752-   #:load-op #:prepare-op #:compile-op
 8.13753-   #:prepare-source-op #:load-source-op #:test-op #:define-op
 8.13754-   #:feature #:version #:version-satisfies #:upgrade-asdf
 8.13755-   #:implementation-identifier #:implementation-type #:hostname
 8.13756-   #:component-depends-on ; backward-compatible name rather than action-depends-on
 8.13757-   #:input-files #:additional-input-files
 8.13758-   #:output-files #:output-file #:perform #:perform-with-restarts
 8.13759-   #:operation-done-p #:explain #:action-description #:component-sideway-dependencies
 8.13760-   #:needed-in-image-p
 8.13761-   #:bundle-op #:monolithic-bundle-op #:precompiled-system #:compiled-file #:bundle-system
 8.13762-   #:program-system
 8.13763-   #:basic-compile-bundle-op #:prepare-bundle-op
 8.13764-   #:compile-bundle-op #:load-bundle-op #:monolithic-compile-bundle-op #:monolithic-load-bundle-op
 8.13765-   #:lib-op #:dll-op #:deliver-asd-op #:program-op #:image-op
 8.13766-   #:monolithic-lib-op #:monolithic-dll-op #:monolithic-deliver-asd-op
 8.13767-   #:concatenate-source-op
 8.13768-   #:load-concatenated-source-op
 8.13769-   #:compile-concatenated-source-op
 8.13770-   #:load-compiled-concatenated-source-op
 8.13771-   #:monolithic-concatenate-source-op
 8.13772-   #:monolithic-load-concatenated-source-op
 8.13773-   #:monolithic-compile-concatenated-source-op
 8.13774-   #:monolithic-load-compiled-concatenated-source-op
 8.13775-   #:operation-monolithic-p
 8.13776-   #:required-components
 8.13777-   #:component-loaded-p
 8.13778-   #:component #:parent-component #:child-component #:system #:module
 8.13779-   #:file-component #:source-file #:c-source-file #:java-source-file
 8.13780-   #:cl-source-file #:cl-source-file.cl #:cl-source-file.lsp
 8.13781-   #:static-file #:doc-file #:html-file
 8.13782-   #:file-type #:source-file-type
 8.13783-   #:register-preloaded-system #:sysdef-preloaded-system-search
 8.13784-   #:register-immutable-system #:sysdef-immutable-system-search
 8.13785-   #:package-inferred-system #:register-system-packages
 8.13786-   #:component-children
 8.13787-   #:component-children-by-name
 8.13788-   #:component-pathname
 8.13789-   #:component-relative-pathname
 8.13790-   #:component-name
 8.13791-   #:component-version
 8.13792-   #:component-parent
 8.13793-   #:component-system
 8.13794-   #:component-encoding
 8.13795-   #:component-external-format
 8.13796-   #:system-description
 8.13797-   #:system-long-description
 8.13798-   #:system-author
 8.13799-   #:system-maintainer
 8.13800-   #:system-license
 8.13801-   #:system-licence
 8.13802-   #:system-version
 8.13803-   #:system-source-file
 8.13804-   #:system-source-directory
 8.13805-   #:system-relative-pathname
 8.13806-   #:system-homepage
 8.13807-   #:system-mailto
 8.13808-   #:system-bug-tracker
 8.13809-   #:system-long-name
 8.13810-   #:system-source-control
 8.13811-   #:map-systems
 8.13812-   #:system-defsystem-depends-on
 8.13813-   #:system-depends-on
 8.13814-   #:system-weakly-depends-on
 8.13815-   #:*system-definition-search-functions*   ; variables
 8.13816-   #:*central-registry*
 8.13817-   #:*compile-file-warnings-behaviour*
 8.13818-   #:*compile-file-failure-behaviour*
 8.13819-   #:*resolve-symlinks*
 8.13820-   #:*verbose-out*
 8.13821-   #:asdf-version
 8.13822-   #:compile-condition #:compile-file-error #:compile-warned-error #:compile-failed-error
 8.13823-   #:compile-warned-warning #:compile-failed-warning
 8.13824-   #:error-name
 8.13825-   #:error-pathname
 8.13826-   #:load-system-definition-error
 8.13827-   #:error-component #:error-operation
 8.13828-   #:system-definition-error
 8.13829-   #:missing-component
 8.13830-   #:missing-component-of-version
 8.13831-   #:missing-dependency
 8.13832-   #:missing-dependency-of-version
 8.13833-   #:circular-dependency        ; errors
 8.13834-   #:duplicate-names #:non-toplevel-system #:non-system-system #:bad-system-name #:system-out-of-date
 8.13835-   #:package-inferred-system-missing-package-error
 8.13836-   #:operation-definition-warning #:operation-definition-error
 8.13837-   #:try-recompiling ; restarts
 8.13838-   #:retry
 8.13839-   #:accept
 8.13840-   #:coerce-entry-to-directory
 8.13841-   #:remove-entry-from-registry
 8.13842-   #:clear-configuration-and-retry
 8.13843-   #:*encoding-detection-hook*
 8.13844-   #:*encoding-external-format-hook*
 8.13845-   #:*default-encoding*
 8.13846-   #:*utf-8-external-format*
 8.13847-   #:clear-configuration
 8.13848-   #:*output-translations-parameter*
 8.13849-   #:initialize-output-translations
 8.13850-   #:disable-output-translations
 8.13851-   #:clear-output-translations
 8.13852-   #:ensure-output-translations
 8.13853-   #:apply-output-translations
 8.13854-   #:compile-file*
 8.13855-   #:compile-file-pathname*
 8.13856-   #:*warnings-file-type* #:enable-deferred-warnings-check #:disable-deferred-warnings-check
 8.13857-   #:enable-asdf-binary-locations-compatibility
 8.13858-   #:*default-source-registries*
 8.13859-   #:*source-registry-parameter*
 8.13860-   #:initialize-source-registry
 8.13861-   #:compute-source-registry
 8.13862-   #:clear-source-registry
 8.13863-   #:ensure-source-registry
 8.13864-   #:process-source-registry
 8.13865-   #:registered-system #:registered-systems #:already-loaded-systems
 8.13866-   #:resolve-location
 8.13867-   #:asdf-message
 8.13868-   #:*user-cache*
 8.13869-   #:user-output-translations-pathname
 8.13870-   #:system-output-translations-pathname
 8.13871-   #:user-output-translations-directory-pathname
 8.13872-   #:system-output-translations-directory-pathname
 8.13873-   #:user-source-registry
 8.13874-   #:system-source-registry
 8.13875-   #:user-source-registry-directory
 8.13876-   #:system-source-registry-directory
 8.13877-
 8.13878-   ;; The symbols below are all DEPRECATED, do not use. To be removed in a further release.
 8.13879-   #:*asdf-verbose* #:run-shell-command
 8.13880-   #:component-load-dependencies #:system-registered-p #:package-system
 8.13881-   #+ecl #:make-build
 8.13882-   #:operation-on-warnings #:operation-on-failure #:operation-error
 8.13883-   #:compile-failed #:compile-warned #:compile-error
 8.13884-   #:module-components #:component-property #:traverse))
 8.13885-;;;; ---------------------------------------------------------------------------
 8.13886-;;;; ASDF-USER, where the action happens.
 8.13887-
 8.13888-(uiop/package:define-package :asdf/user
 8.13889-  (:nicknames :asdf-user)
 8.13890-  ;; NB: releases before 3.1.2 this :use'd only uiop/package instead of uiop below.
 8.13891-  ;; They also :use'd uiop/common-lisp, that reexports common-lisp and is not included in uiop.
 8.13892-  ;; ASDF3 releases from 2.27 to 2.31 called uiop asdf-driver and asdf/foo uiop/foo.
 8.13893-  ;; ASDF1 and ASDF2 releases (2.26 and earlier) create a temporary package
 8.13894-  ;; that only :use's :cl and :asdf
 8.13895-  (:use :uiop/common-lisp :uiop :asdf/interface))
 8.13896-;;;; -----------------------------------------------------------------------
 8.13897-;;;; ASDF Footer: last words and cleanup
 8.13898-
 8.13899-(uiop/package:define-package :asdf/footer
 8.13900-  (:recycle :asdf/footer :asdf)
 8.13901-  (:use :uiop/common-lisp :uiop
 8.13902-        :asdf/system ;; used by ECL
 8.13903-        :asdf/upgrade :asdf/system-registry :asdf/operate :asdf/bundle)
 8.13904-  ;; Happily, all those implementations all have the same module-provider hook interface.
 8.13905-  #+(or abcl clasp cmucl clozure ecl mezzano mkcl sbcl)
 8.13906-  (:import-from #+abcl :sys #+(or clasp cmucl ecl) :ext #+clozure :ccl #+mkcl :mk-ext #+sbcl sb-ext #+mezzano :sys.int
 8.13907-                #:*module-provider-functions*
 8.13908-                #+ecl #:*load-hooks*)
 8.13909-  #+(or clasp mkcl) (:import-from :si #:*load-hooks*))
 8.13910-
 8.13911-(in-package :asdf/footer)
 8.13912-
 8.13913-;;;; Register ASDF itself and all its subsystems as preloaded.
 8.13914-(with-upgradability ()
 8.13915-  (dolist (s '("asdf" "asdf-package-system"))
 8.13916-    ;; Don't bother with these system names, no one relies on them anymore:
 8.13917-    ;; "asdf-utils" "asdf-bundle" "asdf-driver" "asdf-defsystem"
 8.13918-    (register-preloaded-system s :version *asdf-version*))
 8.13919-  (register-preloaded-system "uiop" :version *uiop-version*))
 8.13920-
 8.13921-;;;; Ensure that the version slot on the registered preloaded systems are
 8.13922-;;;; correct, by CLEARing the system. However, we do not CLEAR-SYSTEM
 8.13923-;;;; unconditionally. This is because it's possible the user has upgraded the
 8.13924-;;;; systems using ASDF itself, meaning that the registered systems have real
 8.13925-;;;; data from the file system that we want to preserve instead of blasting
 8.13926-;;;; away and replacing with a blank preloaded system.
 8.13927-(with-upgradability ()
 8.13928-  (unless (equal (system-version (registered-system "asdf")) (asdf-version))
 8.13929-    (clear-system "asdf"))
 8.13930-  ;; 3.1.2 is the last version where asdf-package-system was a separate system.
 8.13931-  (when (version< "3.1.2" (system-version (registered-system "asdf-package-system")))
 8.13932-    (clear-system "asdf-package-system"))
 8.13933-  (unless (equal (system-version (registered-system "uiop")) *uiop-version*)
 8.13934-    (clear-system "uiop")))
 8.13935-
 8.13936-;;;; Hook ASDF into the implementation's REQUIRE and other entry points.
 8.13937-#+(or abcl clasp clisp clozure cmucl ecl mezzano mkcl sbcl)
 8.13938-(with-upgradability ()
 8.13939-  ;; Hook into CL:REQUIRE.
 8.13940-  #-clisp (pushnew 'module-provide-asdf *module-provider-functions*)
 8.13941-  #+clisp (if-let (x (find-symbol* '#:*module-provider-functions* :custom nil))
 8.13942-            (eval `(pushnew 'module-provide-asdf ,x)))
 8.13943-
 8.13944-  #+(or clasp ecl mkcl)
 8.13945-  (progn
 8.13946-    (pushnew '("fasb" . si::load-binary) *load-hooks* :test 'equal :key 'car)
 8.13947-
 8.13948-    #+os-windows
 8.13949-    (unless (assoc "asd" *load-hooks* :test 'equal)
 8.13950-      (appendf *load-hooks* '(("asd" . si::load-source))))
 8.13951-
 8.13952-    ;; Wrap module provider functions in an idempotent, upgrade friendly way
 8.13953-    (defvar *wrapped-module-provider* (make-hash-table))
 8.13954-    (setf (gethash 'module-provide-asdf *wrapped-module-provider*) 'module-provide-asdf)
 8.13955-    (defun wrap-module-provider (provider name)
 8.13956-      (let ((results (multiple-value-list (funcall provider name))))
 8.13957-        (when (first results) (register-preloaded-system (coerce-name name)))
 8.13958-        (values-list results)))
 8.13959-    (defun wrap-module-provider-function (provider)
 8.13960-      (ensure-gethash provider *wrapped-module-provider*
 8.13961-                      (constantly
 8.13962-                       #'(lambda (module-name)
 8.13963-                           (wrap-module-provider provider module-name)))))
 8.13964-    (setf *module-provider-functions*
 8.13965-          (mapcar #'wrap-module-provider-function *module-provider-functions*))))
 8.13966-
 8.13967-#+cmucl ;; Hook into the CMUCL herald.
 8.13968-(with-upgradability ()
 8.13969-  (defun herald-asdf (stream)
 8.13970-    (format stream "    ASDF ~A" (asdf-version)))
 8.13971-  (setf (getf ext:*herald-items* :asdf) '(herald-asdf)))
 8.13972-
 8.13973-
 8.13974-;;;; Done!
 8.13975-(with-upgradability ()
 8.13976-  #+allegro ;; restore *w-o-n-r-c* setting as saved in uiop/common-lisp
 8.13977-  (when (boundp 'excl:*warn-on-nested-reader-conditionals*)
 8.13978-    (setf excl:*warn-on-nested-reader-conditionals* uiop/common-lisp::*acl-warn-save*))
 8.13979-
 8.13980-  ;; Advertise the features we provide.
 8.13981-  (dolist (f '(:asdf :asdf2 :asdf3 :asdf3.1 :asdf3.2 :asdf3.3)) (pushnew f *features*))
 8.13982-
 8.13983-  ;; Provide both lowercase and uppercase, to satisfy more people, especially LispWorks users.
 8.13984-  (provide "asdf") (provide "ASDF")
 8.13985-
 8.13986-  ;; Finally, call a function that will cleanup in case this is an upgrade of an older ASDF.
 8.13987-  (cleanup-upgraded-asdf))
 8.13988-
 8.13989-(when *load-verbose*
 8.13990-  (asdf-message ";; ASDF, version ~a~%" (asdf-version)))
     9.1--- a/tools/prepare-image.lisp	Thu Jun 15 22:01:40 2023 -0400
     9.2+++ b/tools/prepare-image.lisp	Sun Jun 18 22:25:28 2023 -0400
     9.3@@ -3,30 +3,15 @@
     9.4 ;; For SBCL, if you don't have SBCL_HOME set, then we won't be able to require this later.
     9.5 #+sbcl
     9.6 (require 'sb-introspect)
     9.7-
     9.8-(when (probe-file "tools/asdf.lisp")
     9.9-  (format t "Compiling asdf..~%")
    9.10-  (let ((output (compile-file "tools/asdf.lisp" :verbose nil :print nil)))
    9.11-    (load output))
    9.12-  (provide "asdf"))
    9.13-
    9.14+#-sbcl
    9.15 (require "asdf")
    9.16 
    9.17 #+sbcl
    9.18 (require "sb-sprof")
    9.19 
    9.20-#+nil
    9.21-(push (pathname (format nil "~a/local-projects/poiu/" (namestring (uiop:getcwd))))
    9.22-      asdf:*central-registry*)
    9.23-
    9.24-(defvar *asdf-root-guesser* nil)
    9.25-
    9.26-(defparameter *cwd* (merge-pathnames
    9.27-               *default-pathname-defaults*
    9.28-               (uiop:getcwd)))
    9.29+(defvar *cwd* (uiop:getcwd))
    9.30 
    9.31 (defun update-output-translations (root)
    9.32-  "This function is called dynamically from deliver-utils/common.lisp!"
    9.33   (asdf:initialize-output-translations
    9.34    `(:output-translations
    9.35      :inherit-configuration
    9.36@@ -43,69 +28,6 @@
    9.37   (asdf:register-preloaded-system :sb-rotate-byte)
    9.38   (asdf:register-preloaded-system :sb-cltl2))
    9.39 
    9.40-(defun %read-version (file)
    9.41-  (let ((key "version: "))
    9.42-   (loop for line in (uiop:read-file-lines file)
    9.43-         if (string= key line :end2 (length key))
    9.44-           return (subseq line (length key)))))
    9.45-
    9.46-(defun init-quicklisp ()
    9.47-  (let ((version (%read-version "quicklisp/dists/quicklisp/distinfo.txt")))
    9.48-    (let ((quicklisp-loc (ensure-directories-exist
    9.49-                          (merge-pathnames
    9.50-                           (format nil "build/quicklisp/~a/" version)
    9.51-                           *cwd*)))
    9.52-          (src (merge-pathnames
    9.53-                "quicklisp/"
    9.54-                *cwd*)))
    9.55-      (flet ((safe-copy-file (path &optional (dest path))
    9.56-               (let ((src (merge-pathnames
    9.57-                           path
    9.58-                           "quicklisp/"))
    9.59-                     (dest (merge-pathnames
    9.60-                            dest
    9.61-                            quicklisp-loc)))
    9.62-                 (format t "Copying: ~a to ~a~%" src dest)
    9.63-
    9.64-                 (when (equal src dest)
    9.65-                   (error "Trying to overwrite the same file"))
    9.66-                 (unless (uiop:file-exists-p dest)
    9.67-                   (uiop:copy-file
    9.68-                    src
    9.69-                    (ensure-directories-exist
    9.70-                     dest))))))
    9.71-        (loop for name in
    9.72-                       (append (directory
    9.73-                                (merge-pathnames
    9.74-                                 "quicklisp/quicklisp/*.lisp"
    9.75-                                 *cwd*))
    9.76-                               (directory
    9.77-                                (merge-pathnames
    9.78-                                 "quicklisp/quicklisp/*.asd"
    9.79-                                 *cwd*)))
    9.80-              do (safe-copy-file name
    9.81-                                 (format nil "quicklisp/~a.~a"
    9.82-                                         (pathname-name name)
    9.83-                                         (pathname-type name))))
    9.84-        (loop for name in (directory
    9.85-                           (merge-pathnames
    9.86-                            "quicklisp/*.lisp"
    9.87-                            *cwd*))
    9.88-              do (safe-copy-file name
    9.89-                                 (format nil "~a.lisp"
    9.90-                                         (pathname-name name))))
    9.91-        (safe-copy-file "setup.lisp")
    9.92-        (safe-copy-file "quicklisp/version.txt")
    9.93-        (safe-copy-file "dists/quicklisp/distinfo.txt")
    9.94-        (safe-copy-file "dists/quicklisp/enabled.txt")
    9.95-        (safe-copy-file "dists/quicklisp/preference.txt"))
    9.96-      (load (merge-pathnames
    9.97-             "setup.lisp"
    9.98-             quicklisp-loc)))))
    9.99-
   9.100-(init-quicklisp)
   9.101-
   9.102-#+nil
   9.103 (ql:update-all-dists :prompt nil)
   9.104 
   9.105 ;; is the package name already loaded as a feature? uhh look it up
   9.106@@ -117,28 +39,11 @@
   9.107              (when (probe-file dir)
   9.108                (push dir ql:*local-project-directories*)))))
   9.109     #-demo
   9.110-    (push-src-dir "local-projects")
   9.111-    (push-src-dir "src")
   9.112-    (push-src-dir "third-party")
   9.113-    (push-src-dir "lisp")))
   9.114-
   9.115-
   9.116-(defun update-root (cwd)
   9.117-  (update-output-translations cwd)
   9.118-  (update-project-directories cwd))
   9.119+    (push-src-dir ".")
   9.120+    (push-src-dir "vendor")))
   9.121 
   9.122 (update-project-directories *cwd*)
   9.123 
   9.124-(defun maybe-asdf-prepare ()
   9.125-  (when *asdf-root-guesser*
   9.126-    (update-root (funcall *asdf-root-guesser*))))
   9.127-
   9.128-(compile 'maybe-asdf-prepare)
   9.129-
   9.130-(defun unprepare-asdf (root-guesser)
   9.131-  "This function is called dynamically from deliver-utils/common.lisp!"
   9.132-  (setf *asdf-root-guesser* root-guesser))
   9.133-
   9.134 (defun maybe-configure-proxy ()
   9.135   (let ((proxy (uiop:getenv "HTTP_PROXY")))
   9.136     (when (and proxy (> (length proxy) 0))
   9.137@@ -146,15 +51,9 @@
   9.138 
   9.139 (maybe-configure-proxy)
   9.140 
   9.141-
   9.142 (ql:quickload "log4cl")
   9.143 (ql:quickload "prove-asdf")
   9.144 
   9.145 (log:info "*local-project-directories: ~S" ql:*local-project-directories*)
   9.146 
   9.147-;; (ql:quickload :cl-ppcre)
   9.148-;; make sure we have build asd
   9.149-#+nil
   9.150-(push (pathname (format nil "~a/build-utils/" *cwd*))
   9.151-      asdf:*central-registry*)
   9.152 (ql:register-local-projects)