-
Notifications
You must be signed in to change notification settings - Fork 0
/
find-component.lisp
154 lines (129 loc) · 6.86 KB
/
find-component.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
;;;; -------------------------------------------------------------------------
;;;; Finding components
(uiop/package:define-package :asdf/find-component
(:recycle :asdf/find-component :asdf)
(:use :uiop/common-lisp :uiop :asdf/upgrade :asdf/cache
:asdf/component :asdf/system :asdf/find-system)
(:export
#:find-component
#:resolve-dependency-name #:resolve-dependency-spec
#:resolve-dependency-combination
;; Conditions
#:missing-component #:missing-component-of-version #:retry
#:missing-dependency #:missing-dependency-of-version
#:missing-requires #:missing-parent
#:missing-required-by #:missing-version))
(in-package :asdf/find-component)
;;;; Missing component conditions
(with-upgradability ()
(define-condition missing-component-of-version (missing-component)
((version :initform nil :reader missing-version :initarg :version)))
(define-condition missing-dependency (missing-component)
((required-by :initarg :required-by :reader missing-required-by)))
(defmethod print-object ((c missing-dependency) s)
(format s (compatfmt "~@<~A, required by ~A~@:>")
(call-next-method c nil) (missing-required-by c)))
(define-condition missing-dependency-of-version (missing-dependency
missing-component-of-version)
())
(defmethod print-object ((c missing-component) s)
(format s (compatfmt "~@<Component ~S not found~@[ in ~A~]~@:>")
(missing-requires c)
(when (missing-parent c)
(coerce-name (missing-parent c)))))
(defmethod print-object ((c missing-component-of-version) s)
(format s (compatfmt "~@<Component ~S does not match version ~A~@[ in ~A~]~@:>")
(missing-requires c)
(missing-version c)
(when (missing-parent c)
(coerce-name (missing-parent c))))))
;;;; Finding components
(with-upgradability ()
(defgeneric find-component (base path &key registered)
(:documentation "Find a component by resolving the PATH starting from BASE parent.
If REGISTERED is true, only search currently registered systems."))
(defgeneric resolve-dependency-combination (component combinator arguments)
(:documentation "Return a component satisfying the dependency specification (COMBINATOR . ARGUMENTS)
in the context of COMPONENT"))
;; Methods for find-component
;; If the base component is a string, resolve it as a system, then if not nil follow the path.
(defmethod find-component ((base string) path &key registered)
(if-let ((s (if registered
(registered-system base)
(find-system base nil))))
(find-component s path :registered registered)))
;; If the base component is a symbol, coerce it to a name if not nil, and resolve that.
;; If nil, use the path as base if not nil, or else return nil.
(defmethod find-component ((base symbol) path &key registered)
(cond
(base (find-component (coerce-name base) path :registered registered))
(path (find-component path nil :registered registered))
(t nil)))
;; If the base component is a cons cell, resolve its car, and add its cdr to the path.
(defmethod find-component ((base cons) path &key registered)
(find-component (car base) (cons (cdr base) path) :registered registered))
;; If the base component is a parent-component and the path a string, find the named child.
(defmethod find-component ((parent parent-component) (name string) &key registered)
(declare (ignorable registered))
(compute-children-by-name parent :only-if-needed-p t)
(values (gethash name (component-children-by-name parent))))
;; If the path is a symbol, coerce it to a name if non-nil, or else just return the base.
(defmethod find-component (base (name symbol) &key registered)
(if name
(find-component base (coerce-name name) :registered registered)
base))
;; If the path is a cons, first resolve its car as path, then its cdr.
(defmethod find-component ((c component) (name cons) &key registered)
(find-component (find-component c (car name) :registered registered)
(cdr name) :registered registered))
;; If the path is a component, return it, disregarding the base.
(defmethod find-component ((base t) (actual component) &key registered)
(declare (ignorable registered))
actual)
;; Resolve dependency NAME in the context of a COMPONENT, with given optional VERSION constraint.
;; This (private) function is used below by RESOLVE-DEPENDENCY-SPEC and by the :VERSION spec.
(defun resolve-dependency-name (component name &optional version)
(loop
(restart-case
(return
(let ((comp (find-component (component-parent component) name)))
(unless comp
(error 'missing-dependency
:required-by component
:requires name))
(when version
(unless (version-satisfies comp version)
(error 'missing-dependency-of-version
:required-by component
:version version
:requires name)))
comp))
(retry ()
:report (lambda (s)
(format s (compatfmt "~@<Retry loading ~3i~_~A.~@:>") name))
:test
(lambda (c)
(or (null c)
(and (typep c 'missing-dependency)
(eq (missing-required-by c) component)
(equal (missing-requires c) name))))
(unless (component-parent component)
(let ((name (coerce-name name)))
(unset-asdf-cache-entry `(find-system ,name))))))))
;; Resolve dependency specification DEP-SPEC in the context of COMPONENT.
;; This is notably used by MAP-DIRECT-DEPENDENCIES to process the results of COMPONENT-DEPENDS-ON
;; and by PARSE-DEFSYSTEM to process DEFSYSTEM-DEPENDS-ON.
(defun resolve-dependency-spec (component dep-spec)
(let ((component (find-component () component)))
(if (atom dep-spec)
(resolve-dependency-name component dep-spec)
(resolve-dependency-combination component (car dep-spec) (cdr dep-spec)))))
;; Methods for RESOLVE-DEPENDENCY-COMBINATION to parse lists as dependency specifications.
(defmethod resolve-dependency-combination (component combinator arguments)
(parameter-error (compatfmt "~@<In ~S, bad dependency ~S for ~S~@:>")
'resolve-dependency-combination (cons combinator arguments) component))
(defmethod resolve-dependency-combination (component (combinator (eql :feature)) arguments)
(when (featurep (first arguments))
(resolve-dependency-spec component (second arguments))))
(defmethod resolve-dependency-combination (component (combinator (eql :version)) arguments)
(resolve-dependency-name component (first arguments) (second arguments)))) ;; See lp#527788