version.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP
HTML 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))))))))