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