-
Notifications
You must be signed in to change notification settings - Fork 3
/
traversal.lisp
175 lines (153 loc) · 7.28 KB
/
traversal.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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#+xcvb (module (:depends-on ("names" "computations")))
(in-package :xcvb)
(declaim (optimize (speed 2) (safety 3) (debug 3) (compilation-speed 0)))
(defgeneric next-traversal (env spec))
(defgeneric dependency-already-included-p (env grain))
(defgeneric issue-dependency (env grain))
(defgeneric issue-build-command (env command))
(defgeneric issue-link-element (env element))
(defgeneric traversed-dependencies (env))
(defgeneric traversed-build-commands (env))
(defgeneric traversed-link-elements (env command))
(defgeneric build-command-issued-p (env command))
(defgeneric link-element-issued-p (env element))
(defgeneric graph-for (env spec)
(:documentation "Build the dependency graph for given dependency, return the node for it"))
(defgeneric graph-for-atom (env atom))
(defgeneric graph-for-build-module-grain (env grain))
(defgeneric graph-for-lisp (env name))
(defgeneric graph-for-fasls (env name))
(defgeneric graph-for-fasl (env name))
(defgeneric graph-for-cfasl (env name))
(defgeneric graph-for-lisp-object (env name))
(defgeneric graph-for-dynamic-library (env name))
(defgeneric graph-for-static-library (env name))
(defgeneric graph-for-build-libraries (env name))
(defgeneric graph-for-build (env name))
(defgeneric graph-for-compile-build (env name))
(defgeneric graph-for-build-named (env name))
(defgeneric graph-for-image (env name))
(defgeneric graph-for-image-grain (env name pre-image-name dependencies &key))
(defgeneric graph-for-executable (env name))
(defgeneric graph-for-source (env name &key in))
(defgeneric graph-for-asdf (env name))
(defgeneric graph-for-require (env name))
(defgeneric ensure-grain-generated (env grain))
(defgeneric tweak-dependency (env dep))
(defgeneric linking-traversal-p (env))
(defclass traversal (simple-print-object-mixin)
((image-setup
:accessor image-setup
:documentation "xcvb-driver-command options to setup the image for the current world")
(grain-names
:initform nil
:initarg :grain-names
:reader traversed-grain-names-r
:documentation "grain names in the stack of things we try to create -- to avoid circularities")
;; do we also need them as a set? possibly... to be measured.
;; we might benefit from a pure functional set implementation; maybe use fare-utils:pure of FSet
(issued-dependencies
:initform (make-hashset :test 'equal)
:accessor issued-dependencies
:documentation "dependencies issued as part of current computation, as a set")
(traversed-dependencies-r
:initform nil
:accessor traversed-dependencies-r
:documentation "dependencies issued as part of the current computation, in reverse order")))
(defmethod graph-for ((env traversal) spec)
(log-format 10 "Producing graph-for ~S" spec)
(let ((current-grains-r (reverse (traversed-grain-names-r env))))
(let ((mem (member spec current-grains-r :test 'equal)))
(when mem
(user-error
"There is a circularity in the dependencies:~%~{ ~S~% includes~%~} ~S~%"
mem (first mem)))))
(let ((grain (do-graph-for (next-traversal env spec) spec)))
(if (typep grain 'buildable-grain)
(ensure-grain-generated env grain)
(user-error "Grain ~S for ~S is not buildable" grain spec))
grain))
(defmethod ensure-grain-generated (env (grain buildable-grain))
(let ((generator (grain-generator grain)))
(when (and generator (not (and (slot-boundp grain 'computation)
(grain-computation grain))))
(run-generator (next-traversal env (fullname grain)) generator))))
(defun do-graph-for (env spec)
(call-with-grain-registration
spec
#'(lambda () (graph-for-dispatcher env spec))))
(defmethod next-traversal ((env traversal) spec)
(make-instance
(class-of env)
:grain-names (cons spec (traversed-grain-names-r env))))
(defmethod traversed-dependencies ((env traversal))
(reverse (traversed-dependencies-r env)))
(defmethod dependency-already-included-p :before (env grain)
(check-type env traversal)
(check-type grain grain))
(defmethod dependency-already-included-p ((env traversal) grain)
(gethash grain (issued-dependencies env)))
(defmethod issue-dependency :before (env grain)
(check-type env traversal)
(check-type grain grain))
(defmethod issue-dependency ((env traversal) grain)
;; Note: long dependency lists do not mix well with pretty-print.
(log-format-pp 10 "Issuing dependency for ~A" grain)
(setf (gethash grain (issued-dependencies env)) t)
(push grain (traversed-dependencies-r env)))
(defun call-with-dependency-loading (env grain thunk)
(unless (dependency-already-included-p env grain)
(issue-dependency env grain)
(funcall thunk)))
(defmacro with-dependency-loading ((env grain) &body body)
`(call-with-dependency-loading ,env ,grain (lambda () ,@body)))
(define-simple-dispatcher graph-for #'graph-for-atom :generic t)
(defmethod traversed-build-commands ((env traversal))
(reverse (traversed-build-commands-r env)))
(defmethod build-command-issued-p ((env traversal) command)
(values (gethash command (issued-build-commands env))))
(define-graph-for :asdf ((env traversal) system-name)
(declare (ignorable env))
(make-asdf-grain :name system-name
:implementation *lisp-implementation-type*))
(define-graph-for :require ((env traversal) name)
(declare (ignorable env))
(make-require-grain :name name))
(defun handle-target (fullname)
(let* ((target (if fullname
(or (resolve-absolute-module-name fullname)
(resolve-asdf-name fullname))
(let* ((build-file (probe-file "build.xcvb"))
(build-module-grain
(and build-file (pathname-build build-file))))
;; Question: should we make the below error cases warnings,
;; and override conflicts with the current build?
;; NB: User can put . in front of his CL_SOURCE_REGISTRY
;; if that's what he wants.
(etypecase build-module-grain
(build-module-grain
build-module-grain)
(null
(user-error "No build specified, and no build.xcvb in the current directory"))
(invalid-build-registry-entry
(user-error "Implicitly specified build.xcvb in current directory ~
but it is invalid:~%~A~&"
(invalid-build-reason build-module-grain)))))))
(build (typecase target
((or lisp-module-grain executable-grain) (build-module-grain-for target))
(asdf-grain nil)
(t
(user-error "User requested build ~S but it can't be found.~%~
You may check available builds with xcvb ssr.~%" fullname))))
(name (fullname target))
(dep (etypecase target
(build-module-grain `(:build ,name))
(lisp-module-grain `(:fasl ,(second name)))
(asdf-grain name)
(executable-grain name)))
(directory (pathname-directory-pathname
(cond
(build (grain-pathname build))
((typep target 'asdf-grain)
(nth-value 2 (asdf:locate-system (second name))))))))
(values dep (or build target) directory)))