gnu: Add Guile-Bash.
[jackhill/guix/guix.git] / guix / scripts / graph.scm
CommitLineData
88856916 1;;; GNU Guix --- Functional package management for GNU
f88282af 2;;; Copyright © 2015, 2016 Ludovic Courtès <ludo@gnu.org>
88856916
LC
3;;;
4;;; This file is part of GNU Guix.
5;;;
6;;; GNU Guix is free software; you can redistribute it and/or modify it
7;;; under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 3 of the License, or (at
9;;; your option) any later version.
10;;;
11;;; GNU Guix is distributed in the hope that it will be useful, but
12;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19(define-module (guix scripts graph)
20 #:use-module (guix ui)
8fb58371 21 #:use-module (guix graph)
c22a1324 22 #:use-module (guix grafts)
88981dd3 23 #:use-module (guix scripts)
958dd3ce 24 #:use-module (guix combinators)
88856916
LC
25 #:use-module (guix packages)
26 #:use-module (guix monads)
27 #:use-module (guix store)
28 #:use-module (guix gexp)
29 #:use-module (guix derivations)
30 #:use-module ((guix build-system gnu) #:select (standard-packages))
31 #:use-module (gnu packages)
32 #:use-module (guix sets)
88856916 33 #:use-module (srfi srfi-1)
38b92daa 34 #:use-module (srfi srfi-26)
88856916 35 #:use-module (srfi srfi-34)
a773c314 36 #:use-module (srfi srfi-35)
88856916
LC
37 #:use-module (srfi srfi-37)
38 #:use-module (ice-9 match)
88856916
LC
39 #:export (%package-node-type
40 %bag-node-type
38b92daa 41 %bag-with-origins-node-type
88856916
LC
42 %bag-emerged-node-type
43 %derivation-node-type
44 %reference-node-type
7f8fec0f 45 %referrer-node-type
c1a8c5ab
AK
46 %node-types
47
88856916
LC
48 guix-graph))
49
50\f
88856916
LC
51;;;
52;;; Package DAG.
53;;;
54
88856916
LC
55(define (node-full-name thing)
56 "Return a human-readable name to denote THING, a package, origin, or file
57name."
58 (cond ((package? thing)
59 (package-full-name thing))
60 ((origin? thing)
3b4d0103 61 (origin-actual-file-name thing))
88856916
LC
62 ((string? thing) ;file name
63 (or (basename thing)
64 (error "basename" thing)))
65 (else
66 (number->string (object-address thing) 16))))
67
68(define (package-node-edges package)
69 "Return the list of dependencies of PACKAGE."
70 (match (package-direct-inputs package)
71 (((labels packages . outputs) ...)
72 ;; Filter out origins and other non-package dependencies.
73 (filter package? packages))))
74
a773c314
LC
75(define assert-package
76 (match-lambda
77 ((? package? package)
78 package)
79 (x
80 (raise
81 (condition
82 (&message
83 (message (format #f (_ "~a: invalid argument (package name expected)")
84 x))))))))
85
86(define nodes-from-package
87 ;; The default conversion method.
88 (lift1 (compose list assert-package) %store-monad))
89
88856916
LC
90(define %package-node-type
91 ;; Type for the traversal of package nodes.
92 (node-type
93 (name "package")
94 (description "the DAG of packages, excluding implicit inputs")
a773c314 95 (convert nodes-from-package)
88856916
LC
96
97 ;; We use package addresses as unique identifiers. This generally works
98 ;; well, but for generated package objects, we could end up with two
99 ;; packages that are not 'eq?', yet map to the same derivation (XXX).
100 (identifier (lift1 object-address %store-monad))
101 (label node-full-name)
102 (edges (lift1 package-node-edges %store-monad))))
103
104\f
105;;;
106;;; Package DAG using bags.
107;;;
108
109(define (bag-node-identifier thing)
110 "Return a unique identifier for THING, which may be a package, origin, or a
111file name."
112 ;; If THING is a file name (a string), we just return it; if it's a package
113 ;; or origin, we return its address. That gives us the object graph, but
114 ;; that may differ from the derivation graph (for instance,
115 ;; 'package-with-bootstrap-guile' generates fresh package objects, and
116 ;; several packages that are not 'eq?' may actually map to the same
117 ;; derivation.) Thus, we lower THING and use its derivation file name as a
118 ;; unique identifier.
119 (with-monad %store-monad
120 (if (string? thing)
121 (return thing)
122 (mlet %store-monad ((low (lower-object thing)))
123 (return (if (derivation? low)
124 (derivation-file-name low)
125 low))))))
126
127(define (bag-node-edges thing)
38b92daa
LC
128 "Return the list of dependencies of THING, a package or origin.
129Dependencies may include packages, origin, and file names."
130 (cond ((package? thing)
131 (match (bag-direct-inputs (package->bag thing))
132 (((labels things . outputs) ...)
133 things)))
134 ((origin? thing)
51385362 135 (cons (or (origin-patch-guile thing) (default-guile))
38b92daa
LC
136 (if (or (pair? (origin-patches thing))
137 (origin-snippet thing))
138 (match (origin-patch-inputs thing)
139 (#f '())
140 (((labels dependencies _ ...) ...)
141 (delete-duplicates dependencies eq?)))
142 '())))
143 (else
144 '())))
88856916
LC
145
146(define %bag-node-type
147 ;; Type for the traversal of package nodes via the "bag" representation,
148 ;; which includes implicit inputs.
149 (node-type
150 (name "bag")
151 (description "the DAG of packages, including implicit inputs")
a773c314 152 (convert nodes-from-package)
88856916
LC
153 (identifier bag-node-identifier)
154 (label node-full-name)
38b92daa
LC
155 (edges (lift1 (compose (cut filter package? <>) bag-node-edges)
156 %store-monad))))
157
158(define %bag-with-origins-node-type
159 (node-type
160 (name "bag-with-origins")
161 (description "the DAG of packages and origins, including implicit inputs")
a773c314 162 (convert nodes-from-package)
38b92daa
LC
163 (identifier bag-node-identifier)
164 (label node-full-name)
165 (edges (lift1 (lambda (thing)
166 (filter (match-lambda
167 ((? package?) #t)
168 ((? origin?) #t)
169 (_ #f))
170 (bag-node-edges thing)))
171 %store-monad))))
88856916
LC
172
173(define standard-package-set
174 (memoize
175 (lambda ()
176 "Return the set of standard packages provided by GNU-BUILD-SYSTEM."
177 (match (standard-packages)
178 (((labels packages . output) ...)
179 (list->setq packages))))))
180
181(define (bag-node-edges-sans-bootstrap thing)
182 "Like 'bag-node-edges', but pretend that the standard packages of
183GNU-BUILD-SYSTEM have zero dependencies."
184 (if (set-contains? (standard-package-set) thing)
185 '()
186 (bag-node-edges thing)))
187
188(define %bag-emerged-node-type
189 ;; Like %BAG-NODE-TYPE, but without the bootstrap subset of the DAG.
190 (node-type
191 (name "bag-emerged")
192 (description "same as 'bag', but without the bootstrap nodes")
a773c314 193 (convert nodes-from-package)
88856916
LC
194 (identifier bag-node-identifier)
195 (label node-full-name)
f88282af
LC
196 (edges (lift1 (compose (cut filter package? <>)
197 bag-node-edges-sans-bootstrap)
198 %store-monad))))
88856916
LC
199
200\f
201;;;
202;;; Derivation DAG.
203;;;
204
205(define (file->derivation file)
206 "Read the derivation from FILE and return it."
207 (call-with-input-file file read-derivation))
208
209(define (derivation-dependencies obj)
210 "Return the <derivation> objects and store items corresponding to the
211dependencies of OBJ, a <derivation> or store item."
212 (if (derivation? obj)
213 (append (map (compose file->derivation derivation-input-path)
214 (derivation-inputs obj))
215 (derivation-sources obj))
216 '()))
217
218(define (derivation-node-identifier node)
219 "Return a unique identifier for NODE, which may be either a <derivation> or
220a plain store file."
221 (if (derivation? node)
222 (derivation-file-name node)
223 node))
224
225(define (derivation-node-label node)
226 "Return a label for NODE, a <derivation> object or plain store item."
227 (store-path-package-name (match node
228 ((? derivation? drv)
229 (derivation-file-name drv))
230 ((? string? file)
231 file))))
232
233(define %derivation-node-type
234 ;; DAG of derivations. Very accurate, very detailed, but usually too much
235 ;; detailed.
236 (node-type
237 (name "derivation")
238 (description "the DAG of derivations")
a773c314
LC
239 (convert (match-lambda
240 ((? package? package)
241 (with-monad %store-monad
242 (>>= (package->derivation package)
243 (lift1 list %store-monad))))
244 ((? derivation-path? item)
245 (mbegin %store-monad
246 ((store-lift add-temp-root) item)
247 (return (list (file->derivation item)))))
248 (x
249 (raise
250 (condition (&message (message "unsupported argument for \
251derivation graph")))))))
88856916
LC
252 (identifier (lift1 derivation-node-identifier %store-monad))
253 (label derivation-node-label)
254 (edges (lift1 derivation-dependencies %store-monad))))
255
256\f
257;;;
258;;; DAG of residual references (aka. run-time dependencies).
259;;;
260
7f8fec0f
LC
261(define ensure-store-items
262 ;; Return a list of store items as a monadic value based on the given
263 ;; argument, which may be a store item or a package.
264 (match-lambda
265 ((? package? package)
266 ;; Return the output file names of PACKAGE.
267 (mlet %store-monad ((drv (package->derivation package)))
268 (return (match (derivation->output-paths drv)
269 (((_ . file-names) ...)
270 file-names)))))
271 ((? store-path? item)
272 (with-monad %store-monad
273 (return (list item))))
274 (x
275 (raise
276 (condition (&message (message "unsupported argument for \
277this type of graph")))))))
278
88856916
LC
279(define (references* item)
280 "Return as a monadic value the references of ITEM, based either on the
281information available in the local store or using information about
282substitutes."
283 (lambda (store)
284 (guard (c ((nix-protocol-error? c)
285 (match (substitutable-path-info store (list item))
286 ((info)
287 (values (substitutable-references info) store))
288 (()
289 (leave (_ "references for '~a' are not known~%")
290 item)))))
291 (values (references store item) store))))
292
293(define %reference-node-type
294 (node-type
295 (name "references")
296 (description "the DAG of run-time dependencies (store references)")
7f8fec0f 297 (convert ensure-store-items)
88856916
LC
298 (identifier (lift1 identity %store-monad))
299 (label store-path-package-name)
300 (edges references*)))
301
7f8fec0f
LC
302(define non-derivation-referrers
303 (let ((referrers (store-lift referrers)))
304 (lambda (item)
305 "Return the referrers of ITEM, except '.drv' files."
306 (mlet %store-monad ((items (referrers item)))
307 (return (remove derivation-path? items))))))
308
309(define %referrer-node-type
310 (node-type
311 (name "referrers")
312 (description "the DAG of referrers in the store")
313 (convert ensure-store-items)
314 (identifier (lift1 identity %store-monad))
315 (label store-path-package-name)
316 (edges non-derivation-referrers)))
317
88856916
LC
318\f
319;;;
320;;; List of node types.
321;;;
322
323(define %node-types
324 ;; List of all the node types.
325 (list %package-node-type
326 %bag-node-type
38b92daa 327 %bag-with-origins-node-type
88856916
LC
328 %bag-emerged-node-type
329 %derivation-node-type
7f8fec0f
LC
330 %reference-node-type
331 %referrer-node-type))
88856916
LC
332
333(define (lookup-node-type name)
334 "Return the node type called NAME. Raise an error if it is not found."
335 (or (find (lambda (type)
336 (string=? (node-type-name type) name))
337 %node-types)
338 (leave (_ "~a: unknown node type~%") name)))
339
642339dc
RW
340(define (lookup-backend name)
341 "Return the graph backend called NAME. Raise an error if it is not found."
342 (or (find (lambda (backend)
343 (string=? (graph-backend-name backend) name))
344 %graph-backends)
345 (leave (_ "~a: unknown backend~%") name)))
346
88856916
LC
347(define (list-node-types)
348 "Print the available node types along with their synopsis."
349 (display (_ "The available node types are:\n"))
350 (newline)
351 (for-each (lambda (type)
352 (format #t " - ~a: ~a~%"
353 (node-type-name type)
354 (node-type-description type)))
355 %node-types))
356
642339dc
RW
357(define (list-backends)
358 "Print the available backends along with their synopsis."
359 (display (_ "The available backend types are:\n"))
360 (newline)
361 (for-each (lambda (backend)
362 (format #t " - ~a: ~a~%"
363 (graph-backend-name backend)
364 (graph-backend-description backend)))
365 %graph-backends))
366
88856916 367\f
88856916
LC
368;;;
369;;; Command-line options.
370;;;
371
372(define %options
373 (list (option '(#\t "type") #t #f
374 (lambda (opt name arg result)
375 (alist-cons 'node-type (lookup-node-type arg)
376 result)))
377 (option '("list-types") #f #f
378 (lambda (opt name arg result)
379 (list-node-types)
380 (exit 0)))
642339dc
RW
381 (option '(#\b "backend") #t #f
382 (lambda (opt name arg result)
383 (alist-cons 'backend (lookup-backend arg)
384 result)))
385 (option '("list-backends") #f #f
386 (lambda (opt name arg result)
387 (list-backends)
388 (exit 0)))
4c8f997a
LC
389 (option '(#\e "expression") #t #f
390 (lambda (opt name arg result)
391 (alist-cons 'expression arg result)))
88856916
LC
392 (option '(#\h "help") #f #f
393 (lambda args
394 (show-help)
395 (exit 0)))
396 (option '(#\V "version") #f #f
397 (lambda args
398 (show-version-and-exit "guix edit")))))
399
400(define (show-help)
401 ;; TRANSLATORS: Here 'dot' is the name of a program; it must not be
402 ;; translated.
403 (display (_ "Usage: guix graph PACKAGE...
404Emit a Graphviz (dot) representation of the dependencies of PACKAGE...\n"))
405 (display (_ "
642339dc
RW
406 -b, --backend=TYPE produce a graph with the given backend TYPE"))
407 (display (_ "
408 --list-backends list the available graph backends"))
409 (display (_ "
88856916
LC
410 -t, --type=TYPE represent nodes of the given TYPE"))
411 (display (_ "
412 --list-types list the available graph types"))
4c8f997a
LC
413 (display (_ "
414 -e, --expression=EXPR consider the package EXPR evaluates to"))
88856916
LC
415 (newline)
416 (display (_ "
417 -h, --help display this help and exit"))
418 (display (_ "
419 -V, --version display version information and exit"))
420 (newline)
421 (show-bug-report-information))
422
423(define %default-options
642339dc
RW
424 `((node-type . ,%package-node-type)
425 (backend . ,%graphviz-backend)))
88856916
LC
426
427\f
428;;;
429;;; Entry point.
430;;;
431
432(define (guix-graph . args)
433 (with-error-handling
637cd125
LC
434 (let* ((opts (args-fold* args %options
435 (lambda (opt name arg . rest)
436 (leave (_ "~A: unrecognized option~%") name))
437 (lambda (arg result)
438 (alist-cons 'argument arg result))
439 %default-options))
642339dc 440 (backend (assoc-ref opts 'backend))
88856916 441 (type (assoc-ref opts 'node-type))
a773c314
LC
442 (items (filter-map (match-lambda
443 (('argument . (? store-path? item))
444 item)
4c8f997a
LC
445 (('argument . spec)
446 (specification->package spec))
447 (('expression . exp)
448 (read/eval-package-expression exp))
449 (_ #f))
450 opts)))
88856916 451 (with-store store
3cabdead
LC
452 ;; Ask for absolute file names so that .drv file names passed from the
453 ;; user to 'read-derivation' are absolute when it returns.
454 (with-fluids ((%file-port-name-canonicalization 'absolute))
455 (run-with-store store
456 ;; XXX: Since grafting can trigger unsolicited builds, disable it.
457 (mlet %store-monad ((_ (set-grafting #f))
458 (nodes (mapm %store-monad
459 (node-type-convert type)
a773c314 460 items)))
3cabdead
LC
461 (export-graph (concatenate nodes)
462 (current-output-port)
642339dc
RW
463 #:node-type type
464 #:backend backend)))))))
88856916
LC
465 #t)
466
467;;; graph.scm ends here