|
|
- ;;; swank-asdf.lisp -- ASDF support
- ;;
- ;; Authors: Daniel Barlow <dan@telent.net>
- ;; Marco Baringer <mb@bese.it>
- ;; Edi Weitz <edi@agharta.de>
- ;; Francois-Rene Rideau <tunes@google.com>
- ;; and others
- ;; License: Public Domain
- ;;
-
- (in-package :swank)
-
- (eval-when (:compile-toplevel :load-toplevel :execute)
- ;;; The best way to load ASDF is from an init file of an
- ;;; implementation. If ASDF is not loaded at the time swank-asdf is
- ;;; loaded, it will be tried first with (require "asdf"), if that
- ;;; doesn't help and *asdf-path* is set, it will be loaded from that
- ;;; file.
- ;;; To set *asdf-path* put the following into ~/.swank.lisp:
- ;;; (defparameter swank::*asdf-path* #p"/path/to/asdf/asdf.lisp")
- (defvar *asdf-path* nil
- "Path to asdf.lisp file, to be loaded in case (require \"asdf\") fails."))
-
- (eval-when (:compile-toplevel :load-toplevel :execute)
- (unless (member :asdf *features*)
- (ignore-errors (funcall 'require "asdf"))))
-
- (eval-when (:compile-toplevel :load-toplevel :execute)
- (unless (member :asdf *features*)
- (handler-bind ((warning #'muffle-warning))
- (when *asdf-path*
- (load *asdf-path* :if-does-not-exist nil)))))
-
- ;; If still not found, error out.
- (eval-when (:compile-toplevel :load-toplevel :execute)
- (unless (member :asdf *features*)
- (error "Could not load ASDF.
- Please update your implementation or
- install a recent release of ASDF and in your ~~/.swank.lisp specify:
- (defparameter swank::*asdf-path* #p\"/path/containing/asdf/asdf.lisp\")")))
-
- ;;; If ASDF is too old, punt.
- ;; As of January 2014, Quicklisp has been providing 2.26 for a year
- ;; (and previously had 2.014.6 for over a year), whereas
- ;; all SLIME-supported implementations provide ASDF3 (i.e. 2.27 or later)
- ;; except LispWorks (stuck with 2.019) and SCL (which hasn't been released
- ;; in years and doesn't provide ASDF at all, but is fully supported by ASDF).
- ;; If your implementation doesn't provide ASDF, or provides an old one,
- ;; install an upgrade yourself and configure *asdf-path*.
- ;; It's just not worth the hassle supporting something
- ;; that doesn't even have COERCE-PATHNAME.
- ;;
- ;; NB: this version check is duplicated in swank-loader.lisp so that we don't
- ;; try to load this contrib when ASDF is too old since that will abort the SLIME
- ;; connection.
- #-asdf3
- (eval-when (:compile-toplevel :load-toplevel :execute)
- (unless (and #+asdf2 (asdf:version-satisfies (asdf:asdf-version) "2.14.6"))
- (error "Your ASDF is too old. ~
- The oldest version supported by swank-asdf is 2.014.6.")))
- ;;; Import functionality from ASDF that isn't available in all ASDF versions.
- ;;; Please do NOT depend on any of the below as reference:
- ;;; they are sometimes stripped down versions, for compatibility only.
- ;;; Indeed, they are supposed to work on *OLDER*, not *NEWER* versions of ASDF.
- ;;;
- ;;; The way I got these is usually by looking at the current definition,
- ;;; using git blame in one screen to locate which commit last modified it,
- ;;; and git log in another to determine which release that made it in.
- ;;; It is OK for some of the below definitions to be or become obsolete,
- ;;; as long as it will make do with versions older than the tagged version:
- ;;; if ASDF is more recent, its more recent version will win.
- ;;;
- ;;; If your software is hacking ASDF, use its internals.
- ;;; If you want ASDF utilities in user software, please use ASDF-UTILS.
-
- (defun asdf-at-least (version)
- (asdf:version-satisfies (asdf:asdf-version) version))
-
- (defmacro asdefs (version &rest defs)
- (flet ((defun* (version name aname rest)
- `(progn
- (defun ,name ,@rest)
- (declaim (notinline ,name))
- (when (asdf-at-least ,version)
- (setf (fdefinition ',name) (fdefinition ',aname)))))
- (defmethod* (version aname rest)
- `(unless (asdf-at-least ,version)
- (defmethod ,aname ,@rest)))
- (defvar* (name aname rest)
- `(progn
- (define-symbol-macro ,name ,aname)
- (defvar ,aname ,@rest))))
- `(progn
- ,@(loop :for (def name . args) :in defs
- :for aname = (intern (string name) :asdf)
- :collect
- (ecase def
- ((defun) (defun* version name aname args))
- ((defmethod) (defmethod* version aname args))
- ((defvar) (defvar* name aname args)))))))
-
- (asdefs "2.15"
- (defvar *wild* #-cormanlisp :wild #+cormanlisp "*")
-
- (defun collect-asds-in-directory (directory collect)
- (map () collect (directory-asd-files directory)))
-
- (defun register-asd-directory (directory &key recurse exclude collect)
- (if (not recurse)
- (collect-asds-in-directory directory collect)
- (collect-sub*directories-asd-files
- directory :exclude exclude :collect collect))))
-
- (asdefs "2.16"
- (defun load-sysdef (name pathname)
- (declare (ignore name))
- (let ((package (asdf::make-temporary-package)))
- (unwind-protect
- (let ((*package* package)
- (*default-pathname-defaults*
- (asdf::pathname-directory-pathname
- (translate-logical-pathname pathname))))
- (asdf::asdf-message
- "~&; Loading system definition from ~A into ~A~%" ;
- pathname package)
- (load pathname))
- (delete-package package))))
-
- (defun directory* (pathname-spec &rest keys &key &allow-other-keys)
- (apply 'directory pathname-spec
- (append keys
- '#.(or #+allegro
- '(:directories-are-files nil
- :follow-symbolic-links nil)
- #+clozure
- '(:follow-links nil)
- #+clisp
- '(:circle t :if-does-not-exist :ignore)
- #+(or cmu scl)
- '(:follow-links nil :truenamep nil)
- #+sbcl
- (when (find-symbol "RESOLVE-SYMLINKS" '#:sb-impl)
- '(:resolve-symlinks nil)))))))
- (asdefs "2.17"
- (defun collect-sub*directories-asd-files
- (directory &key
- (exclude asdf::*default-source-registry-exclusions*)
- collect)
- (asdf::collect-sub*directories
- directory
- (constantly t)
- (lambda (x) (not (member (car (last (pathname-directory x)))
- exclude :test #'equal)))
- (lambda (dir) (collect-asds-in-directory dir collect))))
-
- (defun system-source-directory (system-designator)
- (asdf::pathname-directory-pathname
- (asdf::system-source-file system-designator)))
-
- (defun filter-logical-directory-results (directory entries merger)
- (if (typep directory 'logical-pathname)
- (loop for f in entries
- when
- (if (typep f 'logical-pathname)
- f
- (let ((u (ignore-errors (funcall merger f))))
- (and u
- (equal (ignore-errors (truename u))
- (truename f))
- u)))
- collect it)
- entries))
-
- (defun directory-asd-files (directory)
- (directory-files directory asdf::*wild-asd*)))
-
- (asdefs "2.19"
- (defun subdirectories (directory)
- (let* ((directory (asdf::ensure-directory-pathname directory))
- #-(or abcl cormanlisp xcl)
- (wild (asdf::merge-pathnames*
- #-(or abcl allegro cmu lispworks sbcl scl xcl)
- asdf::*wild-directory*
- #+(or abcl allegro cmu lispworks sbcl scl xcl) "*.*"
- directory))
- (dirs
- #-(or abcl cormanlisp xcl)
- (ignore-errors
- (directory* wild . #.(or #+clozure '(:directories t :files nil)
- #+mcl '(:directories t))))
- #+(or abcl xcl) (system:list-directory directory)
- #+cormanlisp (cl::directory-subdirs directory))
- #+(or abcl allegro cmu lispworks sbcl scl xcl)
- (dirs (loop for x in dirs
- for d = #+(or abcl xcl) (extensions:probe-directory x)
- #+allegro (excl:probe-directory x)
- #+(or cmu sbcl scl) (asdf::directory-pathname-p x)
- #+lispworks (lw:file-directory-p x)
- when d collect #+(or abcl allegro xcl) d
- #+(or cmu lispworks sbcl scl) x)))
- (filter-logical-directory-results
- directory dirs
- (let ((prefix (or (normalize-pathname-directory-component
- (pathname-directory directory))
- ;; because allegro 8.x returns NIL for #p"FOO:"
- '(:absolute))))
- (lambda (d)
- (let ((dir (normalize-pathname-directory-component
- (pathname-directory d))))
- (and (consp dir) (consp (cdr dir))
- (make-pathname
- :defaults directory :name nil :type nil :version nil
- :directory
- (append prefix
- (make-pathname-component-logical
- (last dir))))))))))))
-
- (asdefs "2.21"
- (defun component-loaded-p (c)
- (and (gethash 'load-op (asdf::component-operation-times
- (asdf::find-component c nil))) t))
-
- (defun normalize-pathname-directory-component (directory)
- (cond
- #-(or cmu sbcl scl)
- ((stringp directory) `(:absolute ,directory) directory)
- ((or (null directory)
- (and (consp directory)
- (member (first directory) '(:absolute :relative))))
- directory)
- (t
- (error "Unrecognized pathname directory component ~S" directory))))
-
- (defun make-pathname-component-logical (x)
- (typecase x
- ((eql :unspecific) nil)
- #+clisp (string (string-upcase x))
- #+clisp (cons (mapcar 'make-pathname-component-logical x))
- (t x)))
-
- (defun make-pathname-logical (pathname host)
- (make-pathname
- :host host
- :directory (make-pathname-component-logical (pathname-directory pathname))
- :name (make-pathname-component-logical (pathname-name pathname))
- :type (make-pathname-component-logical (pathname-type pathname))
- :version (make-pathname-component-logical (pathname-version pathname)))))
-
- (asdefs "2.22"
- (defun directory-files (directory &optional (pattern asdf::*wild-file*))
- (let ((dir (pathname directory)))
- (when (typep dir 'logical-pathname)
- (when (wild-pathname-p dir)
- (error "Invalid wild pattern in logical directory ~S" directory))
- (unless (member (pathname-directory pattern)
- '(() (:relative)) :test 'equal)
- (error "Invalid file pattern ~S for logical directory ~S"
- pattern directory))
- (setf pattern (make-pathname-logical pattern (pathname-host dir))))
- (let ((entries (ignore-errors
- (directory* (asdf::merge-pathnames* pattern dir)))))
- (filter-logical-directory-results
- directory entries
- (lambda (f)
- (make-pathname :defaults dir
- :name (make-pathname-component-logical
- (pathname-name f))
- :type (make-pathname-component-logical
- (pathname-type f))
- :version (make-pathname-component-logical
- (pathname-version f)))))))))
-
- (asdefs "2.26.149"
- (defmethod component-relative-pathname ((system asdf:system))
- (asdf::coerce-pathname
- (and (slot-boundp system 'asdf::relative-pathname)
- (slot-value system 'asdf::relative-pathname))
- :type :directory
- :defaults (system-source-directory system)))
- (defun load-asd (pathname &key name &allow-other-keys)
- (asdf::load-sysdef (or name (string-downcase (pathname-name pathname)))
- pathname)))
-
-
- ;;; Taken from ASDF 1.628
- (defmacro while-collecting ((&rest collectors) &body body)
- `(asdf::while-collecting ,collectors ,@body))
-
- ;;; Now for SLIME-specific stuff
-
- (defun asdf-operation (operation)
- (or (asdf::find-symbol* operation :asdf)
- (error "Couldn't find ASDF operation ~S" operation)))
-
- (defun map-system-components (fn system)
- (map-component-subcomponents fn (asdf:find-system system)))
-
- (defun map-component-subcomponents (fn component)
- (when component
- (funcall fn component)
- (when (typep component 'asdf:module)
- (dolist (c (asdf:module-components component))
- (map-component-subcomponents fn c)))))
-
- ;;; Maintaining a pathname to component table
-
- (defvar *pathname-component* (make-hash-table :test 'equal))
-
- (defun clear-pathname-component-table ()
- (clrhash *pathname-component*))
-
- (defun register-system-pathnames (system)
- (map-system-components 'register-component-pathname system))
-
- (defun recompute-pathname-component-table ()
- (clear-pathname-component-table)
- (asdf::map-systems 'register-system-pathnames))
-
- (defun pathname-component (x)
- (gethash (pathname x) *pathname-component*))
-
- (defmethod asdf:component-pathname :around ((component asdf:component))
- (let ((p (call-next-method)))
- (when (pathnamep p)
- (setf (gethash p *pathname-component*) component))
- p))
-
- (defun register-component-pathname (component)
- (asdf:component-pathname component))
-
- (recompute-pathname-component-table)
-
- ;;; This is a crude hack, see ASDF's LP #481187.
- (defslimefun who-depends-on (system)
- (flet ((system-dependencies (op system)
- (mapcar (lambda (dep)
- (asdf::coerce-name (if (consp dep) (second dep) dep)))
- (cdr (assoc op (asdf:component-depends-on op system))))))
- (let ((system-name (asdf::coerce-name system))
- (result))
- (asdf::map-systems
- (lambda (system)
- (when (member system-name
- (system-dependencies 'asdf:load-op system)
- :test #'string=)
- (push (asdf:component-name system) result))))
- result)))
-
- (defmethod xref-doit ((type (eql :depends-on)) thing)
- (when (typep thing '(or string symbol))
- (loop for dependency in (who-depends-on thing)
- for asd-file = (asdf:system-definition-pathname dependency)
- when asd-file
- collect (list dependency
- (swank/backend:make-location
- `(:file ,(namestring asd-file))
- `(:position 1)
- `(:snippet ,(format nil "(defsystem :~A" dependency)
- :align t))))))
-
- (defslimefun operate-on-system-for-emacs (system-name operation &rest keywords)
- "Compile and load SYSTEM using ASDF.
- Record compiler notes signalled as `compiler-condition's."
- (collect-notes
- (lambda ()
- (apply #'operate-on-system system-name operation keywords))))
-
- (defun operate-on-system (system-name operation-name &rest keyword-args)
- "Perform OPERATION-NAME on SYSTEM-NAME using ASDF.
- The KEYWORD-ARGS are passed on to the operation.
- Example:
- \(operate-on-system \"cl-ppcre\" 'compile-op :force t)"
- (handler-case
- (with-compilation-hooks ()
- (apply #'asdf:operate (asdf-operation operation-name)
- system-name keyword-args)
- t)
- ((or asdf:compile-error #+asdf3 asdf/lisp-build:compile-file-error)
- () nil)))
-
- (defun unique-string-list (&rest lists)
- (sort (delete-duplicates (apply #'append lists) :test #'string=) #'string<))
-
- (defslimefun list-all-systems-in-central-registry ()
- "Returns a list of all systems in ASDF's central registry
- AND in its source-registry. (legacy name)"
- (unique-string-list
- (mapcar
- #'pathname-name
- (while-collecting (c)
- (loop for dir in asdf:*central-registry*
- for defaults = (eval dir)
- when defaults
- do (collect-asds-in-directory defaults #'c))
- (asdf:ensure-source-registry)
- (if (or #+asdf3 t
- #-asdf3 (asdf:version-satisfies (asdf:asdf-version) "2.15"))
- (loop :for k :being :the :hash-keys :of asdf::*source-registry*
- :do (c k))
- #-asdf3
- (dolist (entry (asdf::flatten-source-registry))
- (destructuring-bind (directory &key recurse exclude) entry
- (register-asd-directory
- directory
- :recurse recurse :exclude exclude :collect #'c))))))))
-
- (defslimefun list-all-systems-known-to-asdf ()
- "Returns a list of all systems ASDF knows already."
- (while-collecting (c)
- (asdf::map-systems (lambda (system) (c (asdf:component-name system))))))
-
- (defslimefun list-asdf-systems ()
- "Returns the systems in ASDF's central registry and those which ASDF
- already knows."
- (unique-string-list
- (list-all-systems-known-to-asdf)
- (list-all-systems-in-central-registry)))
-
- (defun asdf-component-source-files (component)
- (while-collecting (c)
- (labels ((f (x)
- (typecase x
- (asdf:source-file (c (asdf:component-pathname x)))
- (asdf:module (map () #'f (asdf:module-components x))))))
- (f component))))
-
- (defun make-operation (x)
- #+#.(swank/backend:with-symbol 'make-operation 'asdf)
- (asdf:make-operation x)
- #-#.(swank/backend:with-symbol 'make-operation 'asdf)
- (make-instance x))
-
- (defun asdf-component-output-files (component)
- (while-collecting (c)
- (labels ((f (x)
- (typecase x
- (asdf:source-file
- (map () #'c
- (asdf:output-files (make-operation 'asdf:compile-op) x)))
- (asdf:module (map () #'f (asdf:module-components x))))))
- (f component))))
-
- (defslimefun asdf-system-files (name)
- (let* ((system (asdf:find-system name))
- (files (mapcar #'namestring
- (cons
- (asdf:system-definition-pathname system)
- (asdf-component-source-files system))))
- (main-file (find name files
- :test #'equalp :key #'pathname-name :start 1)))
- (if main-file
- (cons main-file (remove main-file files
- :test #'equal :count 1))
- files)))
-
- (defslimefun asdf-system-loaded-p (name)
- (component-loaded-p name))
-
- (defslimefun asdf-system-directory (name)
- (namestring (translate-logical-pathname (asdf:system-source-directory name))))
-
- (defun pathname-system (pathname)
- (let ((component (pathname-component pathname)))
- (when component
- (asdf:component-name (asdf:component-system component)))))
-
- (defslimefun asdf-determine-system (file buffer-package-name)
- (or
- (and file
- (pathname-system file))
- (and file
- (progn
- ;; If not found, let's rebuild the table first
- (recompute-pathname-component-table)
- (pathname-system file)))
- ;; If we couldn't find an already defined system,
- ;; try finding a system that's named like BUFFER-PACKAGE-NAME.
- (loop with package = (guess-buffer-package buffer-package-name)
- for name in (package-names package)
- for system = (asdf:find-system (asdf::coerce-name name) nil)
- when (and system
- (or (not file)
- (pathname-system file)))
- return (asdf:component-name system))))
-
- (defslimefun delete-system-fasls (name)
- (let ((removed-count
- (loop for file in (asdf-component-output-files
- (asdf:find-system name))
- when (probe-file file)
- count it
- and
- do (delete-file file))))
- (format nil "~d file~:p ~:*~[were~;was~:;were~] removed" removed-count)))
-
- (defvar *recompile-system* nil)
-
- (defmethod asdf:operation-done-p :around
- ((operation asdf:compile-op)
- component)
- (unless (eql *recompile-system*
- (asdf:component-system component))
- (call-next-method)))
-
- (defslimefun reload-system (name)
- (let ((*recompile-system* (asdf:find-system name)))
- (operate-on-system-for-emacs name 'asdf:load-op)))
-
- ;; Doing list-all-systems-in-central-registry might be quite slow
- ;; since it accesses a file-system, so run it once at the background
- ;; to initialize caches.
- (when (eql *communication-style* :spawn)
- (spawn (lambda ()
- (ignore-errors (list-all-systems-in-central-registry)))
- :name "init-asdf-fs-caches"))
-
- ;;; Hook for compile-file-for-emacs
-
- (defun try-compile-file-with-asdf (pathname load-p &rest options)
- (declare (ignore options))
- (let ((component (pathname-component pathname)))
- (when component
- ;;(format t "~&Compiling ASDF component ~S~%" component)
- (let ((op (make-operation 'asdf:compile-op)))
- (with-compilation-hooks ()
- (asdf:perform op component))
- (when load-p
- (asdf:perform (make-operation 'asdf:load-op) component))
- (values t t nil (first (asdf:output-files op component)))))))
-
- (defun try-compile-asd-file (pathname load-p &rest options)
- (declare (ignore load-p options))
- (when (equalp (pathname-type pathname) "asd")
- (load-asd pathname)
- (values t t nil pathname)))
-
- (pushnew 'try-compile-asd-file *compile-file-for-emacs-hook*)
-
- ;;; (pushnew 'try-compile-file-with-asdf *compile-file-for-emacs-hook*)
-
- (provide :swank-asdf)
|