Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

541 regels
21 KiB

4 jaren geleden
  1. ;;; swank-asdf.lisp -- ASDF support
  2. ;;
  3. ;; Authors: Daniel Barlow <dan@telent.net>
  4. ;; Marco Baringer <mb@bese.it>
  5. ;; Edi Weitz <edi@agharta.de>
  6. ;; Francois-Rene Rideau <tunes@google.com>
  7. ;; and others
  8. ;; License: Public Domain
  9. ;;
  10. (in-package :swank)
  11. (eval-when (:compile-toplevel :load-toplevel :execute)
  12. ;;; The best way to load ASDF is from an init file of an
  13. ;;; implementation. If ASDF is not loaded at the time swank-asdf is
  14. ;;; loaded, it will be tried first with (require "asdf"), if that
  15. ;;; doesn't help and *asdf-path* is set, it will be loaded from that
  16. ;;; file.
  17. ;;; To set *asdf-path* put the following into ~/.swank.lisp:
  18. ;;; (defparameter swank::*asdf-path* #p"/path/to/asdf/asdf.lisp")
  19. (defvar *asdf-path* nil
  20. "Path to asdf.lisp file, to be loaded in case (require \"asdf\") fails."))
  21. (eval-when (:compile-toplevel :load-toplevel :execute)
  22. (unless (member :asdf *features*)
  23. (ignore-errors (funcall 'require "asdf"))))
  24. (eval-when (:compile-toplevel :load-toplevel :execute)
  25. (unless (member :asdf *features*)
  26. (handler-bind ((warning #'muffle-warning))
  27. (when *asdf-path*
  28. (load *asdf-path* :if-does-not-exist nil)))))
  29. ;; If still not found, error out.
  30. (eval-when (:compile-toplevel :load-toplevel :execute)
  31. (unless (member :asdf *features*)
  32. (error "Could not load ASDF.
  33. Please update your implementation or
  34. install a recent release of ASDF and in your ~~/.swank.lisp specify:
  35. (defparameter swank::*asdf-path* #p\"/path/containing/asdf/asdf.lisp\")")))
  36. ;;; If ASDF is too old, punt.
  37. ;; As of January 2014, Quicklisp has been providing 2.26 for a year
  38. ;; (and previously had 2.014.6 for over a year), whereas
  39. ;; all SLIME-supported implementations provide ASDF3 (i.e. 2.27 or later)
  40. ;; except LispWorks (stuck with 2.019) and SCL (which hasn't been released
  41. ;; in years and doesn't provide ASDF at all, but is fully supported by ASDF).
  42. ;; If your implementation doesn't provide ASDF, or provides an old one,
  43. ;; install an upgrade yourself and configure *asdf-path*.
  44. ;; It's just not worth the hassle supporting something
  45. ;; that doesn't even have COERCE-PATHNAME.
  46. ;;
  47. ;; NB: this version check is duplicated in swank-loader.lisp so that we don't
  48. ;; try to load this contrib when ASDF is too old since that will abort the SLIME
  49. ;; connection.
  50. #-asdf3
  51. (eval-when (:compile-toplevel :load-toplevel :execute)
  52. (unless (and #+asdf2 (asdf:version-satisfies (asdf:asdf-version) "2.14.6"))
  53. (error "Your ASDF is too old. ~
  54. The oldest version supported by swank-asdf is 2.014.6.")))
  55. ;;; Import functionality from ASDF that isn't available in all ASDF versions.
  56. ;;; Please do NOT depend on any of the below as reference:
  57. ;;; they are sometimes stripped down versions, for compatibility only.
  58. ;;; Indeed, they are supposed to work on *OLDER*, not *NEWER* versions of ASDF.
  59. ;;;
  60. ;;; The way I got these is usually by looking at the current definition,
  61. ;;; using git blame in one screen to locate which commit last modified it,
  62. ;;; and git log in another to determine which release that made it in.
  63. ;;; It is OK for some of the below definitions to be or become obsolete,
  64. ;;; as long as it will make do with versions older than the tagged version:
  65. ;;; if ASDF is more recent, its more recent version will win.
  66. ;;;
  67. ;;; If your software is hacking ASDF, use its internals.
  68. ;;; If you want ASDF utilities in user software, please use ASDF-UTILS.
  69. (defun asdf-at-least (version)
  70. (asdf:version-satisfies (asdf:asdf-version) version))
  71. (defmacro asdefs (version &rest defs)
  72. (flet ((defun* (version name aname rest)
  73. `(progn
  74. (defun ,name ,@rest)
  75. (declaim (notinline ,name))
  76. (when (asdf-at-least ,version)
  77. (setf (fdefinition ',name) (fdefinition ',aname)))))
  78. (defmethod* (version aname rest)
  79. `(unless (asdf-at-least ,version)
  80. (defmethod ,aname ,@rest)))
  81. (defvar* (name aname rest)
  82. `(progn
  83. (define-symbol-macro ,name ,aname)
  84. (defvar ,aname ,@rest))))
  85. `(progn
  86. ,@(loop :for (def name . args) :in defs
  87. :for aname = (intern (string name) :asdf)
  88. :collect
  89. (ecase def
  90. ((defun) (defun* version name aname args))
  91. ((defmethod) (defmethod* version aname args))
  92. ((defvar) (defvar* name aname args)))))))
  93. (asdefs "2.15"
  94. (defvar *wild* #-cormanlisp :wild #+cormanlisp "*")
  95. (defun collect-asds-in-directory (directory collect)
  96. (map () collect (directory-asd-files directory)))
  97. (defun register-asd-directory (directory &key recurse exclude collect)
  98. (if (not recurse)
  99. (collect-asds-in-directory directory collect)
  100. (collect-sub*directories-asd-files
  101. directory :exclude exclude :collect collect))))
  102. (asdefs "2.16"
  103. (defun load-sysdef (name pathname)
  104. (declare (ignore name))
  105. (let ((package (asdf::make-temporary-package)))
  106. (unwind-protect
  107. (let ((*package* package)
  108. (*default-pathname-defaults*
  109. (asdf::pathname-directory-pathname
  110. (translate-logical-pathname pathname))))
  111. (asdf::asdf-message
  112. "~&; Loading system definition from ~A into ~A~%" ;
  113. pathname package)
  114. (load pathname))
  115. (delete-package package))))
  116. (defun directory* (pathname-spec &rest keys &key &allow-other-keys)
  117. (apply 'directory pathname-spec
  118. (append keys
  119. '#.(or #+allegro
  120. '(:directories-are-files nil
  121. :follow-symbolic-links nil)
  122. #+clozure
  123. '(:follow-links nil)
  124. #+clisp
  125. '(:circle t :if-does-not-exist :ignore)
  126. #+(or cmu scl)
  127. '(:follow-links nil :truenamep nil)
  128. #+sbcl
  129. (when (find-symbol "RESOLVE-SYMLINKS" '#:sb-impl)
  130. '(:resolve-symlinks nil)))))))
  131. (asdefs "2.17"
  132. (defun collect-sub*directories-asd-files
  133. (directory &key
  134. (exclude asdf::*default-source-registry-exclusions*)
  135. collect)
  136. (asdf::collect-sub*directories
  137. directory
  138. (constantly t)
  139. (lambda (x) (not (member (car (last (pathname-directory x)))
  140. exclude :test #'equal)))
  141. (lambda (dir) (collect-asds-in-directory dir collect))))
  142. (defun system-source-directory (system-designator)
  143. (asdf::pathname-directory-pathname
  144. (asdf::system-source-file system-designator)))
  145. (defun filter-logical-directory-results (directory entries merger)
  146. (if (typep directory 'logical-pathname)
  147. (loop for f in entries
  148. when
  149. (if (typep f 'logical-pathname)
  150. f
  151. (let ((u (ignore-errors (funcall merger f))))
  152. (and u
  153. (equal (ignore-errors (truename u))
  154. (truename f))
  155. u)))
  156. collect it)
  157. entries))
  158. (defun directory-asd-files (directory)
  159. (directory-files directory asdf::*wild-asd*)))
  160. (asdefs "2.19"
  161. (defun subdirectories (directory)
  162. (let* ((directory (asdf::ensure-directory-pathname directory))
  163. #-(or abcl cormanlisp xcl)
  164. (wild (asdf::merge-pathnames*
  165. #-(or abcl allegro cmu lispworks sbcl scl xcl)
  166. asdf::*wild-directory*
  167. #+(or abcl allegro cmu lispworks sbcl scl xcl) "*.*"
  168. directory))
  169. (dirs
  170. #-(or abcl cormanlisp xcl)
  171. (ignore-errors
  172. (directory* wild . #.(or #+clozure '(:directories t :files nil)
  173. #+mcl '(:directories t))))
  174. #+(or abcl xcl) (system:list-directory directory)
  175. #+cormanlisp (cl::directory-subdirs directory))
  176. #+(or abcl allegro cmu lispworks sbcl scl xcl)
  177. (dirs (loop for x in dirs
  178. for d = #+(or abcl xcl) (extensions:probe-directory x)
  179. #+allegro (excl:probe-directory x)
  180. #+(or cmu sbcl scl) (asdf::directory-pathname-p x)
  181. #+lispworks (lw:file-directory-p x)
  182. when d collect #+(or abcl allegro xcl) d
  183. #+(or cmu lispworks sbcl scl) x)))
  184. (filter-logical-directory-results
  185. directory dirs
  186. (let ((prefix (or (normalize-pathname-directory-component
  187. (pathname-directory directory))
  188. ;; because allegro 8.x returns NIL for #p"FOO:"
  189. '(:absolute))))
  190. (lambda (d)
  191. (let ((dir (normalize-pathname-directory-component
  192. (pathname-directory d))))
  193. (and (consp dir) (consp (cdr dir))
  194. (make-pathname
  195. :defaults directory :name nil :type nil :version nil
  196. :directory
  197. (append prefix
  198. (make-pathname-component-logical
  199. (last dir))))))))))))
  200. (asdefs "2.21"
  201. (defun component-loaded-p (c)
  202. (and (gethash 'load-op (asdf::component-operation-times
  203. (asdf::find-component c nil))) t))
  204. (defun normalize-pathname-directory-component (directory)
  205. (cond
  206. #-(or cmu sbcl scl)
  207. ((stringp directory) `(:absolute ,directory) directory)
  208. ((or (null directory)
  209. (and (consp directory)
  210. (member (first directory) '(:absolute :relative))))
  211. directory)
  212. (t
  213. (error "Unrecognized pathname directory component ~S" directory))))
  214. (defun make-pathname-component-logical (x)
  215. (typecase x
  216. ((eql :unspecific) nil)
  217. #+clisp (string (string-upcase x))
  218. #+clisp (cons (mapcar 'make-pathname-component-logical x))
  219. (t x)))
  220. (defun make-pathname-logical (pathname host)
  221. (make-pathname
  222. :host host
  223. :directory (make-pathname-component-logical (pathname-directory pathname))
  224. :name (make-pathname-component-logical (pathname-name pathname))
  225. :type (make-pathname-component-logical (pathname-type pathname))
  226. :version (make-pathname-component-logical (pathname-version pathname)))))
  227. (asdefs "2.22"
  228. (defun directory-files (directory &optional (pattern asdf::*wild-file*))
  229. (let ((dir (pathname directory)))
  230. (when (typep dir 'logical-pathname)
  231. (when (wild-pathname-p dir)
  232. (error "Invalid wild pattern in logical directory ~S" directory))
  233. (unless (member (pathname-directory pattern)
  234. '(() (:relative)) :test 'equal)
  235. (error "Invalid file pattern ~S for logical directory ~S"
  236. pattern directory))
  237. (setf pattern (make-pathname-logical pattern (pathname-host dir))))
  238. (let ((entries (ignore-errors
  239. (directory* (asdf::merge-pathnames* pattern dir)))))
  240. (filter-logical-directory-results
  241. directory entries
  242. (lambda (f)
  243. (make-pathname :defaults dir
  244. :name (make-pathname-component-logical
  245. (pathname-name f))
  246. :type (make-pathname-component-logical
  247. (pathname-type f))
  248. :version (make-pathname-component-logical
  249. (pathname-version f)))))))))
  250. (asdefs "2.26.149"
  251. (defmethod component-relative-pathname ((system asdf:system))
  252. (asdf::coerce-pathname
  253. (and (slot-boundp system 'asdf::relative-pathname)
  254. (slot-value system 'asdf::relative-pathname))
  255. :type :directory
  256. :defaults (system-source-directory system)))
  257. (defun load-asd (pathname &key name &allow-other-keys)
  258. (asdf::load-sysdef (or name (string-downcase (pathname-name pathname)))
  259. pathname)))
  260. ;;; Taken from ASDF 1.628
  261. (defmacro while-collecting ((&rest collectors) &body body)
  262. `(asdf::while-collecting ,collectors ,@body))
  263. ;;; Now for SLIME-specific stuff
  264. (defun asdf-operation (operation)
  265. (or (asdf::find-symbol* operation :asdf)
  266. (error "Couldn't find ASDF operation ~S" operation)))
  267. (defun map-system-components (fn system)
  268. (map-component-subcomponents fn (asdf:find-system system)))
  269. (defun map-component-subcomponents (fn component)
  270. (when component
  271. (funcall fn component)
  272. (when (typep component 'asdf:module)
  273. (dolist (c (asdf:module-components component))
  274. (map-component-subcomponents fn c)))))
  275. ;;; Maintaining a pathname to component table
  276. (defvar *pathname-component* (make-hash-table :test 'equal))
  277. (defun clear-pathname-component-table ()
  278. (clrhash *pathname-component*))
  279. (defun register-system-pathnames (system)
  280. (map-system-components 'register-component-pathname system))
  281. (defun recompute-pathname-component-table ()
  282. (clear-pathname-component-table)
  283. (asdf::map-systems 'register-system-pathnames))
  284. (defun pathname-component (x)
  285. (gethash (pathname x) *pathname-component*))
  286. (defmethod asdf:component-pathname :around ((component asdf:component))
  287. (let ((p (call-next-method)))
  288. (when (pathnamep p)
  289. (setf (gethash p *pathname-component*) component))
  290. p))
  291. (defun register-component-pathname (component)
  292. (asdf:component-pathname component))
  293. (recompute-pathname-component-table)
  294. ;;; This is a crude hack, see ASDF's LP #481187.
  295. (defslimefun who-depends-on (system)
  296. (flet ((system-dependencies (op system)
  297. (mapcar (lambda (dep)
  298. (asdf::coerce-name (if (consp dep) (second dep) dep)))
  299. (cdr (assoc op (asdf:component-depends-on op system))))))
  300. (let ((system-name (asdf::coerce-name system))
  301. (result))
  302. (asdf::map-systems
  303. (lambda (system)
  304. (when (member system-name
  305. (system-dependencies 'asdf:load-op system)
  306. :test #'string=)
  307. (push (asdf:component-name system) result))))
  308. result)))
  309. (defmethod xref-doit ((type (eql :depends-on)) thing)
  310. (when (typep thing '(or string symbol))
  311. (loop for dependency in (who-depends-on thing)
  312. for asd-file = (asdf:system-definition-pathname dependency)
  313. when asd-file
  314. collect (list dependency
  315. (swank/backend:make-location
  316. `(:file ,(namestring asd-file))
  317. `(:position 1)
  318. `(:snippet ,(format nil "(defsystem :~A" dependency)
  319. :align t))))))
  320. (defslimefun operate-on-system-for-emacs (system-name operation &rest keywords)
  321. "Compile and load SYSTEM using ASDF.
  322. Record compiler notes signalled as `compiler-condition's."
  323. (collect-notes
  324. (lambda ()
  325. (apply #'operate-on-system system-name operation keywords))))
  326. (defun operate-on-system (system-name operation-name &rest keyword-args)
  327. "Perform OPERATION-NAME on SYSTEM-NAME using ASDF.
  328. The KEYWORD-ARGS are passed on to the operation.
  329. Example:
  330. \(operate-on-system \"cl-ppcre\" 'compile-op :force t)"
  331. (handler-case
  332. (with-compilation-hooks ()
  333. (apply #'asdf:operate (asdf-operation operation-name)
  334. system-name keyword-args)
  335. t)
  336. ((or asdf:compile-error #+asdf3 asdf/lisp-build:compile-file-error)
  337. () nil)))
  338. (defun unique-string-list (&rest lists)
  339. (sort (delete-duplicates (apply #'append lists) :test #'string=) #'string<))
  340. (defslimefun list-all-systems-in-central-registry ()
  341. "Returns a list of all systems in ASDF's central registry
  342. AND in its source-registry. (legacy name)"
  343. (unique-string-list
  344. (mapcar
  345. #'pathname-name
  346. (while-collecting (c)
  347. (loop for dir in asdf:*central-registry*
  348. for defaults = (eval dir)
  349. when defaults
  350. do (collect-asds-in-directory defaults #'c))
  351. (asdf:ensure-source-registry)
  352. (if (or #+asdf3 t
  353. #-asdf3 (asdf:version-satisfies (asdf:asdf-version) "2.15"))
  354. (loop :for k :being :the :hash-keys :of asdf::*source-registry*
  355. :do (c k))
  356. #-asdf3
  357. (dolist (entry (asdf::flatten-source-registry))
  358. (destructuring-bind (directory &key recurse exclude) entry
  359. (register-asd-directory
  360. directory
  361. :recurse recurse :exclude exclude :collect #'c))))))))
  362. (defslimefun list-all-systems-known-to-asdf ()
  363. "Returns a list of all systems ASDF knows already."
  364. (while-collecting (c)
  365. (asdf::map-systems (lambda (system) (c (asdf:component-name system))))))
  366. (defslimefun list-asdf-systems ()
  367. "Returns the systems in ASDF's central registry and those which ASDF
  368. already knows."
  369. (unique-string-list
  370. (list-all-systems-known-to-asdf)
  371. (list-all-systems-in-central-registry)))
  372. (defun asdf-component-source-files (component)
  373. (while-collecting (c)
  374. (labels ((f (x)
  375. (typecase x
  376. (asdf:source-file (c (asdf:component-pathname x)))
  377. (asdf:module (map () #'f (asdf:module-components x))))))
  378. (f component))))
  379. (defun make-operation (x)
  380. #+#.(swank/backend:with-symbol 'make-operation 'asdf)
  381. (asdf:make-operation x)
  382. #-#.(swank/backend:with-symbol 'make-operation 'asdf)
  383. (make-instance x))
  384. (defun asdf-component-output-files (component)
  385. (while-collecting (c)
  386. (labels ((f (x)
  387. (typecase x
  388. (asdf:source-file
  389. (map () #'c
  390. (asdf:output-files (make-operation 'asdf:compile-op) x)))
  391. (asdf:module (map () #'f (asdf:module-components x))))))
  392. (f component))))
  393. (defslimefun asdf-system-files (name)
  394. (let* ((system (asdf:find-system name))
  395. (files (mapcar #'namestring
  396. (cons
  397. (asdf:system-definition-pathname system)
  398. (asdf-component-source-files system))))
  399. (main-file (find name files
  400. :test #'equalp :key #'pathname-name :start 1)))
  401. (if main-file
  402. (cons main-file (remove main-file files
  403. :test #'equal :count 1))
  404. files)))
  405. (defslimefun asdf-system-loaded-p (name)
  406. (component-loaded-p name))
  407. (defslimefun asdf-system-directory (name)
  408. (namestring (translate-logical-pathname (asdf:system-source-directory name))))
  409. (defun pathname-system (pathname)
  410. (let ((component (pathname-component pathname)))
  411. (when component
  412. (asdf:component-name (asdf:component-system component)))))
  413. (defslimefun asdf-determine-system (file buffer-package-name)
  414. (or
  415. (and file
  416. (pathname-system file))
  417. (and file
  418. (progn
  419. ;; If not found, let's rebuild the table first
  420. (recompute-pathname-component-table)
  421. (pathname-system file)))
  422. ;; If we couldn't find an already defined system,
  423. ;; try finding a system that's named like BUFFER-PACKAGE-NAME.
  424. (loop with package = (guess-buffer-package buffer-package-name)
  425. for name in (package-names package)
  426. for system = (asdf:find-system (asdf::coerce-name name) nil)
  427. when (and system
  428. (or (not file)
  429. (pathname-system file)))
  430. return (asdf:component-name system))))
  431. (defslimefun delete-system-fasls (name)
  432. (let ((removed-count
  433. (loop for file in (asdf-component-output-files
  434. (asdf:find-system name))
  435. when (probe-file file)
  436. count it
  437. and
  438. do (delete-file file))))
  439. (format nil "~d file~:p ~:*~[were~;was~:;were~] removed" removed-count)))
  440. (defvar *recompile-system* nil)
  441. (defmethod asdf:operation-done-p :around
  442. ((operation asdf:compile-op)
  443. component)
  444. (unless (eql *recompile-system*
  445. (asdf:component-system component))
  446. (call-next-method)))
  447. (defslimefun reload-system (name)
  448. (let ((*recompile-system* (asdf:find-system name)))
  449. (operate-on-system-for-emacs name 'asdf:load-op)))
  450. ;; Doing list-all-systems-in-central-registry might be quite slow
  451. ;; since it accesses a file-system, so run it once at the background
  452. ;; to initialize caches.
  453. (when (eql *communication-style* :spawn)
  454. (spawn (lambda ()
  455. (ignore-errors (list-all-systems-in-central-registry)))
  456. :name "init-asdf-fs-caches"))
  457. ;;; Hook for compile-file-for-emacs
  458. (defun try-compile-file-with-asdf (pathname load-p &rest options)
  459. (declare (ignore options))
  460. (let ((component (pathname-component pathname)))
  461. (when component
  462. ;;(format t "~&Compiling ASDF component ~S~%" component)
  463. (let ((op (make-operation 'asdf:compile-op)))
  464. (with-compilation-hooks ()
  465. (asdf:perform op component))
  466. (when load-p
  467. (asdf:perform (make-operation 'asdf:load-op) component))
  468. (values t t nil (first (asdf:output-files op component)))))))
  469. (defun try-compile-asd-file (pathname load-p &rest options)
  470. (declare (ignore load-p options))
  471. (when (equalp (pathname-type pathname) "asd")
  472. (load-asd pathname)
  473. (values t t nil pathname)))
  474. (pushnew 'try-compile-asd-file *compile-file-for-emacs-hook*)
  475. ;;; (pushnew 'try-compile-file-with-asdf *compile-file-for-emacs-hook*)
  476. (provide :swank-asdf)