dotfiles/sbcl/.quicklisp/quicklisp/impl-util.lisp

341 lines
12 KiB
Common Lisp
Raw Normal View History

2020-01-20 14:13:08 -05:00
;;;; impl-util.lisp
(in-package #:ql-impl-util)
(definterface call-with-quiet-compilation (fun)
(:documentation
"Call FUN with warnings, style-warnings, and other verbose messages
suppressed.")
(:implementation t
(let ((*load-verbose* nil)
(*compile-verbose* nil)
(*load-print* nil)
(*compile-print* nil))
(handler-bind ((warning #'muffle-warning))
(funcall fun)))))
(defimplementation (call-with-quiet-compilation :for sbcl :qualifier :around)
(fun)
(declare (ignore fun))
(handler-bind ((ql-sbcl:compiler-note #'muffle-warning))
(call-next-method)))
(defimplementation (call-with-quiet-compilation :for cmucl :qualifier :around)
(fun)
(declare (ignore fun))
(let ((ql-cmucl:*gc-verbose* nil))
(call-next-method)))
(definterface rename-directory (from to)
(:implementation t
(rename-file from to)
(truename to))
(:implementation cmucl
(rename-file from (string-right-trim "/" (namestring to)))
(truename to))
(:implementation clisp
(ql-clisp:rename-directory from to)
(truename to)))
(definterface probe-directory (pathname)
(:documentation "Return the truename of PATHNAME, if it exists and
is a directory, or NIL otherwise.")
(:implementation t
(let ((directory (probe-file pathname)))
(when directory
;; probe-file is specified to return the truename of the path,
;; but Allegro does not return the truename; truenamize it.
(truename directory))))
(:implementation clisp
(let ((directory (ql-clisp:probe-pathname pathname)))
(when (and directory (ql-clisp:probe-directory directory))
directory))))
(definterface init-file-name ()
(:documentation "Return the init file name for the current implementation.")
(:implementation allegro
".clinit.cl")
(:implementation abcl
".abclrc")
(:implementation ccl
#+windows
"ccl-init.lisp"
#-windows
".ccl-init.lisp")
(:implementation clasp
".clasprc")
(:implementation clisp
".clisprc.lisp")
(:implementation ecl
".eclrc")
(:implementation mkcl
".mkclrc")
(:implementation lispworks
".lispworks")
(:implementation sbcl
".sbclrc")
(:implementation cmucl
".cmucl-init.lisp")
(:implementation scl
".scl-init.lisp")
)
(defun init-file-name-for (&optional implementation-designator)
(let* ((class-name (find-symbol (string-upcase implementation-designator)
'ql-impl))
(class (find-class class-name nil)))
(when class
(let ((*implementation* (make-instance class)))
(init-file-name)))))
(defun quicklisp-init-file-form ()
"Return a form suitable for describing the location of the quicklisp
init file. If the file is available relative to the home directory,
returns a form that merges with the home directory instead of
specifying an absolute file."
(let* ((init-file (ql-setup:qmerge "setup.lisp"))
(enough (enough-namestring init-file (user-homedir-pathname))))
(cond ((equal (pathname enough) (pathname init-file))
;; The init-file is somewhere outside of the home directory
(pathname enough))
(t
`(merge-pathnames ,enough (user-homedir-pathname))))))
(defun write-init-forms (stream &key (indentation 0))
(format stream "~%~v@T;;; The following lines added by ql:add-to-init-file:~%"
indentation)
(format stream "~v@T#-quicklisp~%" indentation)
(let ((*print-case* :downcase))
(format stream "~v@T(let ((quicklisp-init ~S))~%"
indentation
(quicklisp-init-file-form)))
(format stream "~v@T (when (probe-file quicklisp-init)~%" indentation)
(format stream "~v@T (load quicklisp-init)))~%~%" indentation))
(defun suitable-lisp-init-file (implementation)
"Return the name of IMPLEMENTATION's init file. If IMPLEMENTAION is
a string or pathname, return its merged pathname instead."
(typecase implementation
((or string pathname)
(merge-pathnames implementation))
((or null (eql t))
(init-file-name))
(t
(init-file-name-for implementation))))
(defun add-to-init-file (&optional implementation-or-file)
"Add forms to the Lisp implementation's init file that will load
quicklisp at CL startup."
(let ((init-file (suitable-lisp-init-file implementation-or-file)))
(unless init-file
(error "Don't know how to add to init file for your implementation."))
(setf init-file (merge-pathnames init-file (user-homedir-pathname)))
(format *query-io* "~&I will append the following lines to ~S:~%"
init-file)
(write-init-forms *query-io* :indentation 2)
(when (ql-util:press-enter-to-continue)
(with-open-file (stream init-file
:direction :output
:if-does-not-exist :create
:if-exists :append)
(write-init-forms stream)))
init-file))
;;;
;;; Native namestrings.
;;;
(definterface native-namestring (pathname)
(:documentation "In Clozure CL, #\\.s in pathname-names are escaped
in namestrings with #\\> on Windows and #\\\\ elsewhere. This can
cause a problem when using CL:NAMESTRING to store pathname data that
might be used by other implementations. NATIVE-NAMESTRING is
intended to provide a namestring that can be parsed as a same-enough
object on multiple implementations.")
(:implementation t
(namestring pathname))
(:implementation ccl
(ql-ccl:native-translated-namestring pathname))
(:implementation sbcl
(ql-sbcl:native-namestring pathname)))
;;;
;;; Directory write date
;;;
(definterface directory-write-date (pathname)
(:documentation "Return the write-date of the directory designated
by PATHNAME as a universal time, like file-write-date.")
(:implementation t
(file-write-date pathname))
(:implementation clisp
(nth-value 2 (ql-clisp:probe-pathname pathname))))
;;;
;;; Deleting a directory tree
;;;
(defvar *wild-entry*
(make-pathname :name :wild :type :wild :version :wild))
(defvar *wild-relative*
(make-pathname :directory '(:relative :wild)))
(definterface directoryp (entry)
(:documentation "Return true if ENTRY refers to a directory.")
(:implementation t
(not (or (pathname-name entry)
(pathname-type entry))))
(:implementation allegro
(ql-allegro:file-directory-p entry))
(:implementation lispworks
(ql-lispworks:file-directory-p entry)))
(definterface directory-entries (directory)
(:documentation "Return all directory entries of DIRECTORY as a
list, or NIL if there are no directory entries. Excludes the \".\"
and \"..\" entries.")
(:implementation allegro
(directory directory
#+allegro :directories-are-files
#+allegro nil
#+allegro :follow-symbolic-links
#+allegro nil))
(:implementation abcl
(directory (merge-pathnames *wild-entry* directory)
#+abcl :resolve-symlinks #+abcl nil))
(:implementation ccl
(directory (merge-pathnames *wild-entry* directory)
#+ccl :directories #+ccl t
#+ccl :follow-links #+ccl nil))
(:implementation clasp
(nconc
(directory (merge-pathnames *wild-entry* directory)
#+clasp :resolve-symlinks #+clasp nil)
(directory (merge-pathnames *wild-relative* directory)
#+clasp :resolve-symlinks #+clasp nil)))
(:implementation clisp
;; :full gives pathnames as well as truenames, BUT: it returns a
;; singleton pathname, not a list, on dead symlinks.
(remove nil
(mapcar (lambda (entry) (and (listp entry) (first entry)))
(nconc (directory (merge-pathnames *wild-entry* directory)
#+clisp :full #+clisp t
#+clisp :if-does-not-exist #+clisp :keep)
(directory (merge-pathnames *wild-relative* directory)
#+clisp :full #+clisp t
#+clisp :if-does-not-exist #+clisp :keep)))))
(:implementation cmucl
(directory (merge-pathnames *wild-entry* directory)
#+cmucl :truenamep #+cmucl nil))
(:implementation scl
(directory (merge-pathnames *wild-entry* directory)
#+scl :truenamep #+scl nil))
(:implementation lispworks
(directory (merge-pathnames *wild-entry* directory)
#+lispworks :directories #+lispworks t
#+lispworks :link-transparency #+lispworks nil))
(:implementation ecl
(nconc
(directory (merge-pathnames *wild-entry* directory)
#+ecl :resolve-symlinks #+ecl nil)
(directory (merge-pathnames *wild-relative* directory)
#+ecl :resolve-symlinks #+ecl nil)))
(:implementation mkcl
(setf directory (truename directory))
(nconc
(directory (merge-pathnames *wild-entry* directory))
(directory (merge-pathnames *wild-relative* directory))))
(:implementation sbcl
(directory (merge-pathnames *wild-entry* directory)
#+sbcl :resolve-symlinks #+sbcl nil)))
(defimplementation (directory-entries :qualifier :around) (directory)
;; Don't return any entries when called with a non-directory
;; argument
(if (directoryp directory)
(call-next-method)
(warn "directory-entries - not a directory -- ~S" directory)))
(definterface delete-directory (entry)
(:documentation "Delete the directory ENTRY. Might signal an error
if it is not an empty directory.")
(:implementation t
(delete-file entry))
(:implementation allegro
(ql-allegro:delete-directory entry))
(:implementation ccl
(ql-ccl:delete-directory entry))
(:implementation clasp
(ql-clasp:rmdir entry))
(:implementation clisp
(ql-clisp:delete-directory entry))
(:implementation cmucl
(ql-cmucl:unix-rmdir (namestring entry)))
(:implementation scl
(ql-scl:unix-rmdir (ql-scl:unix-namestring entry)))
(:implementation ecl
(ql-ecl:rmdir entry))
(:implementation mkcl
(ql-mkcl:rmdir entry))
(:implementation lispworks
(ql-lispworks:delete-directory entry))
(:implementation sbcl
(ql-sbcl:rmdir entry)))
(defimplementation (delete-directory :qualifier :around) (directory)
;; Don't delete non-directories with delete-directory
(if (directoryp directory)
(call-next-method)
(error "delete-directory - not a directory -- ~A" directory)))
(definterface delete-directory-tree (pathname)
(:documentation "Delete the directory tree rooted at PATHNAME.")
(:implementation t
(let ((directories-to-process (list (truename pathname)))
(directories-to-delete '()))
(loop
(unless directories-to-process
(return))
(let* ((current (pop directories-to-process))
(entries (directory-entries current)))
(push current directories-to-delete)
(dolist (entry entries)
(if (directoryp entry)
(push entry directories-to-process)
(delete-file entry)))))
(map nil 'delete-directory directories-to-delete)))
(:implementation allegro
(ql-allegro:delete-directory-and-files pathname))
(:implementation ccl
(ql-ccl:delete-directory pathname)))
(defimplementation (delete-directory-tree :qualifier :around) (pathname)
(if (directoryp pathname)
(call-next-method)
(progn
(warn "delete-directory-tree - not a directory, ~
deleting anyway -- ~s" pathname)
(delete-file pathname))))
(defun map-directory-tree (directory fun)
"Call FUN for every file in directory and all its subdirectories,
recursively. Uses the truename of directory as a starting point. Does
not follow symlinks, but, on some implementations, DOES include
potentially dead symlinks."
(let ((directories-to-process (list (truename directory))))
(loop
(unless directories-to-process
(return))
(let* ((current (pop directories-to-process))
(entries (directory-entries current)))
(dolist (entry entries)
(if (directoryp entry)
(push entry directories-to-process)
(funcall fun entry)))))))