version.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP (HTM) git clone git://bitreich.org/clic/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/clic/ (DIR) Log (DIR) Files (DIR) Refs (DIR) Tags (DIR) README (DIR) LICENSE --- version.lisp (9383B) --- 1 (uiop/package:define-package :uiop/version 2 (:recycle :uiop/version :uiop/utility :asdf) 3 (:use :uiop/common-lisp :uiop/package :uiop/utility) 4 (:export 5 #:*uiop-version* 6 #:parse-version #:unparse-version #:version< #:version<= ;; version support, moved from uiop/utility 7 #:next-version 8 #:deprecated-function-condition #:deprecated-function-name ;; deprecation control 9 #:deprecated-function-style-warning #:deprecated-function-warning 10 #:deprecated-function-error #:deprecated-function-should-be-deleted 11 #:version-deprecation #:with-deprecation)) 12 (in-package :uiop/version) 13 14 (with-upgradability () 15 (defparameter *uiop-version* "3.3.4") 16 17 (defun unparse-version (version-list) 18 "From a parsed version (a list of natural numbers), compute the version string" 19 (format nil "~{~D~^.~}" version-list)) 20 21 (defun parse-version (version-string &optional on-error) 22 "Parse a VERSION-STRING as a series of natural numbers separated by dots. 23 Return a (non-null) list of integers if the string is valid; 24 otherwise return NIL. 25 26 When invalid, ON-ERROR is called as per CALL-FUNCTION before to return NIL, 27 with format arguments explaining why the version is invalid. 28 ON-ERROR is also called if the version is not canonical 29 in that it doesn't print back to itself, but the list is returned anyway." 30 (block nil 31 (unless (stringp version-string) 32 (call-function on-error "~S: ~S is not a string" 'parse-version version-string) 33 (return)) 34 (unless (loop :for prev = nil :then c :for c :across version-string 35 :always (or (digit-char-p c) 36 (and (eql c #\.) prev (not (eql prev #\.)))) 37 :finally (return (and c (digit-char-p c)))) 38 (call-function on-error "~S: ~S doesn't follow asdf version numbering convention" 39 'parse-version version-string) 40 (return)) 41 (let* ((version-list 42 (mapcar #'parse-integer (split-string version-string :separator "."))) 43 (normalized-version (unparse-version version-list))) 44 (unless (equal version-string normalized-version) 45 (call-function on-error "~S: ~S contains leading zeros" 'parse-version version-string)) 46 version-list))) 47 48 (defun next-version (version) 49 "When VERSION is not nil, it is a string, then parse it as a version, compute the next version 50 and return it as a string." 51 (when version 52 (let ((version-list (parse-version version))) 53 (incf (car (last version-list))) 54 (unparse-version version-list)))) 55 56 (defun version< (version1 version2) 57 "Given two version strings, return T if the second is strictly newer" 58 (let ((v1 (parse-version version1 nil)) 59 (v2 (parse-version version2 nil))) 60 (lexicographic< '< v1 v2))) 61 62 (defun version<= (version1 version2) 63 "Given two version strings, return T if the second is newer or the same" 64 (not (version< version2 version1)))) 65 66 67 (with-upgradability () 68 (define-condition deprecated-function-condition (condition) 69 ((name :initarg :name :reader deprecated-function-name))) 70 (define-condition deprecated-function-style-warning (deprecated-function-condition style-warning) ()) 71 (define-condition deprecated-function-warning (deprecated-function-condition warning) ()) 72 (define-condition deprecated-function-error (deprecated-function-condition error) ()) 73 (define-condition deprecated-function-should-be-deleted (deprecated-function-condition error) ()) 74 75 (defun deprecated-function-condition-kind (type) 76 (ecase type 77 ((deprecated-function-style-warning) :style-warning) 78 ((deprecated-function-warning) :warning) 79 ((deprecated-function-error) :error) 80 ((deprecated-function-should-be-deleted) :delete))) 81 82 (defmethod print-object ((c deprecated-function-condition) stream) 83 (let ((name (deprecated-function-name c))) 84 (cond 85 (*print-readably* 86 (let ((fmt "#.(make-condition '~S :name ~S)") 87 (args (list (type-of c) name))) 88 (if *read-eval* 89 (apply 'format stream fmt args) 90 (error "Can't print ~?" fmt args)))) 91 (*print-escape* 92 (print-unreadable-object (c stream :type t) (format stream ":name ~S" name))) 93 (t 94 (let ((*package* (find-package :cl)) 95 (type (type-of c))) 96 (format stream 97 (if (eq type 'deprecated-function-should-be-deleted) 98 "~A: Still defining deprecated function~:P ~{~S~^ ~} that promised to delete" 99 "~A: Using deprecated function ~S -- please update your code to use a newer API.~ 100 ~@[~%The docstring for this function says:~%~A~%~]") 101 type name (when (symbolp name) (documentation name 'function)))))))) 102 103 (defun notify-deprecated-function (status name) 104 (ecase status 105 ((nil) nil) 106 ((:style-warning) (style-warn 'deprecated-function-style-warning :name name)) 107 ((:warning) (warn 'deprecated-function-warning :name name)) 108 ((:error) (cerror "USE FUNCTION ANYWAY" 'deprecated-function-error :name name)))) 109 110 (defun version-deprecation (version &key (style-warning nil) 111 (warning (next-version style-warning)) 112 (error (next-version warning)) 113 (delete (next-version error))) 114 "Given a VERSION string, and the starting versions for notifying the programmer of 115 various levels of deprecation, return the current level of deprecation as per WITH-DEPRECATION 116 that is the highest level that has a declared version older than the specified version. 117 Each start version for a level of deprecation can be specified by a keyword argument, or 118 if left unspecified, will be the NEXT-VERSION of the immediate lower level of deprecation." 119 (cond 120 ((and delete (version<= delete version)) :delete) 121 ((and error (version<= error version)) :error) 122 ((and warning (version<= warning version)) :warning) 123 ((and style-warning (version<= style-warning version)) :style-warning))) 124 125 (defmacro with-deprecation ((level) &body definitions) 126 "Given a deprecation LEVEL (a form to be EVAL'ed at macro-expansion time), instrument the 127 DEFUN and DEFMETHOD forms in DEFINITIONS to notify the programmer of the deprecation of the function 128 when it is compiled or called. 129 130 Increasing levels (as result from evaluating LEVEL) are: NIL (not deprecated yet), 131 :STYLE-WARNING (a style warning is issued when used), :WARNING (a full warning is issued when used), 132 :ERROR (a continuable error instead), and :DELETE (it's an error if the code is still there while 133 at that level). 134 135 Forms other than DEFUN and DEFMETHOD are not instrumented, and you can protect a DEFUN or DEFMETHOD 136 from instrumentation by enclosing it in a PROGN." 137 (let ((level (eval level))) 138 (check-type level (member nil :style-warning :warning :error :delete)) 139 (when (eq level :delete) 140 (error 'deprecated-function-should-be-deleted :name 141 (mapcar 'second 142 (remove-if-not #'(lambda (x) (member x '(defun defmethod))) 143 definitions :key 'first)))) 144 (labels ((instrument (name head body whole) 145 (if level 146 (let ((notifiedp 147 (intern (format nil "*~A-~A-~A-~A*" 148 :deprecated-function level name :notified-p)))) 149 (multiple-value-bind (remaining-forms declarations doc-string) 150 (parse-body body :documentation t :whole whole) 151 `(progn 152 (defparameter ,notifiedp nil) 153 ;; tell some implementations to use the compiler-macro 154 (declaim (inline ,name)) 155 (define-compiler-macro ,name (&whole form &rest args) 156 (declare (ignore args)) 157 (notify-deprecated-function ,level ',name) 158 form) 159 (,@head ,@(when doc-string (list doc-string)) ,@declarations 160 (unless ,notifiedp 161 (setf ,notifiedp t) 162 (notify-deprecated-function ,level ',name)) 163 ,@remaining-forms)))) 164 `(progn 165 (eval-when (:compile-toplevel :load-toplevel :execute) 166 (setf (compiler-macro-function ',name) nil)) 167 (declaim (notinline ,name)) 168 (,@head ,@body))))) 169 `(progn 170 ,@(loop :for form :in definitions :collect 171 (cond 172 ((and (consp form) (eq (car form) 'defun)) 173 (instrument (second form) (subseq form 0 3) (subseq form 3) form)) 174 ((and (consp form) (eq (car form) 'defmethod)) 175 (let ((body-start (if (listp (third form)) 3 4))) 176 (instrument (second form) 177 (subseq form 0 body-start) 178 (subseq form body-start) 179 form))) 180 (t 181 form))))))))