;;;; 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)))))))