Klimi's new dotfiles with stow.
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

376 lines
14 KiB

  1. ;;;; -*- indent-tabs-mode: nil -*-
  2. ;;;
  3. ;;; swank-loader.lisp --- Compile and load the Slime backend.
  4. ;;;
  5. ;;; Created 2003, James Bielman <jamesjb@jamesjb.com>
  6. ;;;
  7. ;;; This code has been placed in the Public Domain. All warranties
  8. ;;; are disclaimed.
  9. ;;;
  10. ;; If you want customize the source- or fasl-directory you can set
  11. ;; swank-loader:*source-directory* resp. swank-loader:*fasl-directory*
  12. ;; before loading this files.
  13. ;; E.g.:
  14. ;;
  15. ;; (load ".../swank-loader.lisp")
  16. ;; (setq swank-loader::*fasl-directory* "/tmp/fasl/")
  17. ;; (swank-loader:init)
  18. (cl:defpackage :swank-loader
  19. (:use :cl)
  20. (:export :init
  21. :dump-image
  22. :list-fasls
  23. :*source-directory*
  24. :*fasl-directory*
  25. :*started-from-emacs*))
  26. (cl:in-package :swank-loader)
  27. (defvar *started-from-emacs* nil)
  28. (defvar *source-directory*
  29. (make-pathname :name nil :type nil
  30. :defaults (or *load-pathname* *default-pathname-defaults*))
  31. "The directory where to look for the source.")
  32. (defparameter *sysdep-files*
  33. #+cmu '((swank source-path-parser) (swank source-file-cache) (swank cmucl)
  34. (swank gray))
  35. #+scl '((swank source-path-parser) (swank source-file-cache) (swank scl)
  36. (swank gray))
  37. #+sbcl '((swank source-path-parser) (swank source-file-cache) (swank sbcl)
  38. (swank gray))
  39. #+clozure '(metering (swank ccl) (swank gray))
  40. #+lispworks '((swank lispworks) (swank gray))
  41. #+allegro '((swank allegro) (swank gray))
  42. #+clisp '(xref metering (swank clisp) (swank gray))
  43. #+armedbear '((swank abcl))
  44. #+cormanlisp '((swank corman) (swank gray))
  45. #+ecl '((swank ecl) (swank gray))
  46. #+clasp '((swank clasp) (swank gray))
  47. #+mkcl '((swank mkcl) (swank gray))
  48. #+mezzano '((swank mezzano) (swank gray))
  49. )
  50. (defparameter *implementation-features*
  51. '(:allegro :lispworks :sbcl :clozure :cmu :clisp :ccl :corman :cormanlisp
  52. :armedbear :gcl :ecl :scl :mkcl :clasp :mezzano))
  53. (defparameter *os-features*
  54. '(:macosx :linux :windows :mswindows :win32 :solaris :darwin :sunos :hpux
  55. :unix :mezzano))
  56. (defparameter *architecture-features*
  57. '(:powerpc :ppc :x86 :x86-64 :x86_64 :amd64 :i686 :i586 :i486 :pc386 :iapx386
  58. :sparc64 :sparc :hppa64 :hppa :arm :armv5l :armv6l :armv7l :arm64 :aarch64
  59. :pentium3 :pentium4
  60. :mips :mipsel
  61. :java-1.4 :java-1.5 :java-1.6 :java-1.7))
  62. (defun q (s) (read-from-string s))
  63. #+ecl
  64. (defun ecl-version-string ()
  65. (format nil "~A~@[-~A~]"
  66. (lisp-implementation-version)
  67. (when (find-symbol "LISP-IMPLEMENTATION-VCS-ID" :ext)
  68. (let ((vcs-id (funcall (q "ext:lisp-implementation-vcs-id"))))
  69. (when (>= (length vcs-id) 8)
  70. (subseq vcs-id 0 8))))))
  71. #+clasp
  72. (defun clasp-version-string ()
  73. (format nil "~A~@[-~A~]"
  74. (lisp-implementation-version)
  75. (core:lisp-implementation-id)))
  76. (defun lisp-version-string ()
  77. #+(or clozure cmu) (substitute-if #\_ (lambda (x) (find x " /"))
  78. (lisp-implementation-version))
  79. #+(or cormanlisp scl mkcl) (lisp-implementation-version)
  80. #+sbcl (format nil "~a~:[~;-no-threads~]"
  81. (lisp-implementation-version)
  82. #+sb-thread nil
  83. #-sb-thread t)
  84. #+lispworks (lisp-implementation-version)
  85. #+allegro (format nil "~@{~a~}"
  86. excl::*common-lisp-version-number*
  87. (if (eq 'h 'H) "A" "M") ; ANSI vs MoDeRn
  88. (if (member :smp *features*) "s" "")
  89. (if (member :64bit *features*) "-64bit" "")
  90. (excl:ics-target-case
  91. (:-ics "")
  92. (:+ics "-ics")))
  93. #+clisp (let ((s (lisp-implementation-version)))
  94. (subseq s 0 (position #\space s)))
  95. #+armedbear (lisp-implementation-version)
  96. #+ecl (ecl-version-string)
  97. #+clasp (clasp-version-string)
  98. #+mezzano (let ((s (lisp-implementation-version)))
  99. (subseq s 0 (position #\space s))))
  100. (defun unique-dir-name ()
  101. "Return a name that can be used as a directory name that is
  102. unique to a Lisp implementation, Lisp implementation version,
  103. operating system, and hardware architecture."
  104. (flet ((first-of (features)
  105. (loop for f in features
  106. when (find f *features*) return it))
  107. (maybe-warn (value fstring &rest args)
  108. (cond (value)
  109. (t (apply #'warn fstring args)
  110. "unknown"))))
  111. (let ((lisp (maybe-warn (first-of *implementation-features*)
  112. "No implementation feature found in ~a."
  113. *implementation-features*))
  114. (os (maybe-warn (first-of *os-features*)
  115. "No os feature found in ~a." *os-features*))
  116. (arch (maybe-warn (first-of *architecture-features*)
  117. "No architecture feature found in ~a."
  118. *architecture-features*))
  119. (version (maybe-warn (lisp-version-string)
  120. "Don't know how to get Lisp ~
  121. implementation version.")))
  122. (format nil "~(~@{~a~^-~}~)" lisp version os arch))))
  123. (defun file-newer-p (new-file old-file)
  124. "Returns true if NEW-FILE is newer than OLD-FILE."
  125. (> (file-write-date new-file) (file-write-date old-file)))
  126. (defun string-starts-with (string prefix)
  127. (string-equal string prefix :end1 (min (length string) (length prefix))))
  128. (defun slime-version-string ()
  129. "Return a string identifying the SLIME version.
  130. Return nil if nothing appropriate is available."
  131. (with-open-file (s (merge-pathnames "slime.el" *source-directory*)
  132. :if-does-not-exist nil)
  133. (when s
  134. (loop with prefix = ";; Version: "
  135. for line = (read-line s nil :eof)
  136. until (eq line :eof)
  137. when (string-starts-with line prefix)
  138. return (subseq line (length prefix))))))
  139. (defun default-fasl-dir ()
  140. (merge-pathnames
  141. (make-pathname
  142. :directory `(:relative ".slime" "fasl"
  143. ,@(if (slime-version-string) (list (slime-version-string)))
  144. ,(unique-dir-name)))
  145. (user-homedir-pathname)))
  146. (defvar *fasl-directory* (default-fasl-dir)
  147. "The directory where fasl files should be placed.")
  148. (defun binary-pathname (src-pathname binary-dir)
  149. "Return the pathname where SRC-PATHNAME's binary should be compiled."
  150. (let ((cfp (compile-file-pathname src-pathname)))
  151. (merge-pathnames (make-pathname :name (pathname-name cfp)
  152. :type (pathname-type cfp))
  153. binary-dir)))
  154. (defun handle-swank-load-error (condition context pathname)
  155. (fresh-line *error-output*)
  156. (pprint-logical-block (*error-output* () :per-line-prefix ";; ")
  157. (format *error-output*
  158. "~%Error ~A ~A:~% ~A~%"
  159. context pathname condition)))
  160. (defun compile-files (files fasl-dir load quiet)
  161. "Compile each file in FILES if the source is newer than its
  162. corresponding binary, or the file preceding it was recompiled.
  163. If LOAD is true, load the fasl file."
  164. (let ((needs-recompile nil)
  165. (state :unknown))
  166. (dolist (src files)
  167. (let ((dest (binary-pathname src fasl-dir)))
  168. (handler-bind
  169. ((error (lambda (c)
  170. (ecase state
  171. (:compile (handle-swank-load-error c "compiling" src))
  172. (:load (handle-swank-load-error c "loading" dest))
  173. (:unknown (handle-swank-load-error c "???ing" src))))))
  174. (when (or needs-recompile
  175. (not (probe-file dest))
  176. (file-newer-p src dest))
  177. (ensure-directories-exist dest)
  178. ;; need to recompile SRC, so we'll need to recompile
  179. ;; everything after this too.
  180. (setf needs-recompile t
  181. state :compile)
  182. (or (compile-file src :output-file dest :print nil
  183. :verbose (not quiet))
  184. ;; An implementation may not necessarily signal a
  185. ;; condition itself when COMPILE-FILE fails (e.g. ECL)
  186. (error "COMPILE-FILE returned NIL.")))
  187. (when load
  188. (setf state :load)
  189. (load dest :verbose (not quiet))))))))
  190. #+cormanlisp
  191. (defun compile-files (files fasl-dir load quiet)
  192. "Corman Lisp has trouble with compiled files."
  193. (declare (ignore fasl-dir))
  194. (when load
  195. (dolist (file files)
  196. (load file :verbose (not quiet)
  197. (force-output)))))
  198. (defun load-user-init-file ()
  199. "Load the user init file, return NIL if it does not exist."
  200. (load (merge-pathnames (user-homedir-pathname)
  201. (make-pathname :name ".swank" :type "lisp"))
  202. :if-does-not-exist nil))
  203. (defun load-site-init-file (dir)
  204. (load (make-pathname :name "site-init" :type "lisp"
  205. :defaults dir)
  206. :if-does-not-exist nil))
  207. (defun src-files (names src-dir)
  208. (mapcar (lambda (name)
  209. (multiple-value-bind (dirs name)
  210. (etypecase name
  211. (symbol (values '() name))
  212. (cons (values (butlast name) (car (last name)))))
  213. (make-pathname
  214. :directory (append (or (pathname-directory src-dir)
  215. '(:relative))
  216. (mapcar #'string-downcase dirs))
  217. :name (string-downcase name)
  218. :type "lisp"
  219. :defaults src-dir)))
  220. names))
  221. (defvar *swank-files*
  222. `(packages
  223. (swank backend) ,@*sysdep-files* (swank match) (swank rpc)
  224. swank))
  225. (defvar *contribs*
  226. '(swank-util swank-repl
  227. swank-c-p-c swank-arglists swank-fuzzy
  228. swank-fancy-inspector
  229. swank-presentations swank-presentation-streams
  230. #+(or asdf2 asdf3 sbcl ecl) swank-asdf
  231. swank-package-fu
  232. swank-hyperdoc
  233. #+sbcl swank-sbcl-exts
  234. swank-mrepl
  235. swank-trace-dialog
  236. swank-macrostep
  237. swank-quicklisp)
  238. "List of names for contrib modules.")
  239. (defun append-dir (absolute name)
  240. (merge-pathnames
  241. (make-pathname :directory `(:relative ,name) :defaults absolute)
  242. absolute))
  243. (defun contrib-dir (base-dir)
  244. (append-dir base-dir "contrib"))
  245. (defun load-swank (&key (src-dir *source-directory*)
  246. (fasl-dir *fasl-directory*)
  247. quiet)
  248. (with-compilation-unit ()
  249. (compile-files (src-files *swank-files* src-dir) fasl-dir t quiet))
  250. (funcall (q "swank::before-init")
  251. (slime-version-string)
  252. (list (contrib-dir fasl-dir)
  253. (contrib-dir src-dir))))
  254. (defun delete-stale-contrib-fasl-files (swank-files contrib-files fasl-dir)
  255. (let ((newest (reduce #'max (mapcar #'file-write-date swank-files))))
  256. (dolist (src contrib-files)
  257. (let ((fasl (binary-pathname src fasl-dir)))
  258. (when (and (probe-file fasl)
  259. (<= (file-write-date fasl) newest))
  260. (delete-file fasl))))))
  261. (defun compile-contribs (&key (src-dir (contrib-dir *source-directory*))
  262. (fasl-dir (contrib-dir *fasl-directory*))
  263. (swank-src-dir *source-directory*)
  264. load quiet)
  265. (let* ((swank-src-files (src-files *swank-files* swank-src-dir))
  266. (contrib-src-files (src-files *contribs* src-dir)))
  267. (delete-stale-contrib-fasl-files swank-src-files contrib-src-files
  268. fasl-dir)
  269. (compile-files contrib-src-files fasl-dir load quiet)))
  270. (defun loadup ()
  271. (load-swank)
  272. (compile-contribs :load t))
  273. (defun setup ()
  274. (load-site-init-file *source-directory*)
  275. (load-user-init-file)
  276. (when (#-clisp probe-file
  277. #+clisp ext:probe-directory
  278. (contrib-dir *source-directory*))
  279. (eval `(pushnew 'compile-contribs ,(q "swank::*after-init-hook*"))))
  280. (funcall (q "swank::init")))
  281. (defun list-swank-packages ()
  282. (remove-if-not (lambda (package)
  283. (let ((name (package-name package)))
  284. (and (string-not-equal name "swank-loader")
  285. (string-starts-with name "swank"))))
  286. (list-all-packages)))
  287. (defun delete-packages (packages)
  288. (dolist (package packages)
  289. (flet ((handle-package-error (c)
  290. (let ((pkgs (set-difference (package-used-by-list package)
  291. packages)))
  292. (when pkgs
  293. (warn "deleting ~a which is used by ~{~a~^, ~}."
  294. package pkgs))
  295. (continue c))))
  296. (handler-bind ((package-error #'handle-package-error))
  297. (delete-package package)))))
  298. (defun init (&key delete reload load-contribs (setup t)
  299. (quiet (not *load-verbose*))
  300. from-emacs)
  301. "Load SWANK and initialize some global variables.
  302. If DELETE is true, delete any existing SWANK packages.
  303. If RELOAD is true, reload SWANK, even if the SWANK package already exists.
  304. If LOAD-CONTRIBS is true, load all contribs
  305. If SETUP is true, load user init files and initialize some
  306. global variabes in SWANK."
  307. (when from-emacs
  308. (setf *started-from-emacs* t))
  309. (when (and delete (find-package :swank))
  310. (delete-packages (list-swank-packages)))
  311. (cond ((or (not (find-package :swank)) reload)
  312. (load-swank :quiet quiet))
  313. (t
  314. (warn "Not reloading SWANK. Package already exists.")))
  315. (when load-contribs
  316. (compile-contribs :load t :quiet quiet))
  317. (when setup
  318. (setup)))
  319. (defun dump-image (filename)
  320. (init :setup nil)
  321. (funcall (q "swank/backend:save-image") filename))
  322. (defun list-fasls (&key (include-contribs t) (compile t)
  323. (quiet (not *compile-verbose*)))
  324. "List up SWANK's fasls along with their dependencies."
  325. (flet ((collect-fasls (files fasl-dir)
  326. (when compile
  327. (compile-files files fasl-dir nil quiet))
  328. (loop for src in files
  329. when (probe-file (binary-pathname src fasl-dir))
  330. collect it)))
  331. (append (collect-fasls (src-files *swank-files* *source-directory*)
  332. *fasl-directory*)
  333. (when include-contribs
  334. (collect-fasls (src-files *contribs*
  335. (contrib-dir *source-directory*))
  336. (contrib-dir *fasl-directory*))))))