gnu: surgescript: Update to 0.5.4.4.
[jackhill/guix/guix.git] / guix / scripts / graph.scm
CommitLineData
88856916 1;;; GNU Guix --- Functional package management for GNU
09238d61 2;;; Copyright © 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
ee9a735b 3;;; Copyright © 2019 Simon Tournier <zimon.toutoune@gmail.com>
88856916
LC
4;;;
5;;; This file is part of GNU Guix.
6;;;
7;;; GNU Guix is free software; you can redistribute it and/or modify it
8;;; under the terms of the GNU General Public License as published by
9;;; the Free Software Foundation; either version 3 of the License, or (at
10;;; your option) any later version.
11;;;
12;;; GNU Guix is distributed in the hope that it will be useful, but
13;;; WITHOUT ANY WARRANTY; without even the implied warranty of
14;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;;; GNU General Public License for more details.
16;;;
17;;; You should have received a copy of the GNU General Public License
18;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
19
20(define-module (guix scripts graph)
21 #:use-module (guix ui)
8fb58371 22 #:use-module (guix graph)
c22a1324 23 #:use-module (guix grafts)
88981dd3 24 #:use-module (guix scripts)
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)
f9704f17 30 #:use-module (guix memoization)
b06a70e0 31 #:use-module (guix modules)
88856916
LC
32 #:use-module ((guix build-system gnu) #:select (standard-packages))
33 #:use-module (gnu packages)
34 #:use-module (guix sets)
d51bfe24
LC
35 #:use-module ((guix diagnostics)
36 #:select (location-file formatted-message))
3e962e59
LC
37 #:use-module ((guix scripts build)
38 #:select (show-transformation-options-help
39 options->transformation
ee9a735b 40 %standard-build-options
3e962e59 41 %transformation-options))
88856916 42 #:use-module (srfi srfi-1)
38b92daa 43 #:use-module (srfi srfi-26)
88856916 44 #:use-module (srfi srfi-34)
a773c314 45 #:use-module (srfi srfi-35)
88856916 46 #:use-module (srfi srfi-37)
fdbba544 47 #:use-module (ice-9 format)
88856916 48 #:use-module (ice-9 match)
88856916 49 #:export (%package-node-type
b96a0640 50 %reverse-package-node-type
88856916 51 %bag-node-type
38b92daa 52 %bag-with-origins-node-type
88856916 53 %bag-emerged-node-type
2b81eac0 54 %reverse-bag-node-type
88856916
LC
55 %derivation-node-type
56 %reference-node-type
7f8fec0f 57 %referrer-node-type
b06a70e0 58 %module-node-type
c1a8c5ab
AK
59 %node-types
60
88856916
LC
61 guix-graph))
62
63\f
88856916
LC
64;;;
65;;; Package DAG.
66;;;
67
88856916
LC
68(define (node-full-name thing)
69 "Return a human-readable name to denote THING, a package, origin, or file
70name."
71 (cond ((package? thing)
72 (package-full-name thing))
73 ((origin? thing)
3b4d0103 74 (origin-actual-file-name thing))
88856916
LC
75 ((string? thing) ;file name
76 (or (basename thing)
77 (error "basename" thing)))
78 (else
79 (number->string (object-address thing) 16))))
80
81(define (package-node-edges package)
82 "Return the list of dependencies of PACKAGE."
83 (match (package-direct-inputs package)
84 (((labels packages . outputs) ...)
85 ;; Filter out origins and other non-package dependencies.
86 (filter package? packages))))
87
a773c314
LC
88(define assert-package
89 (match-lambda
90 ((? package? package)
91 package)
92 (x
93 (raise
d51bfe24
LC
94 (formatted-message (G_ "~a: invalid argument (package name expected)")
95 x)))))
a773c314
LC
96
97(define nodes-from-package
98 ;; The default conversion method.
99 (lift1 (compose list assert-package) %store-monad))
100
88856916
LC
101(define %package-node-type
102 ;; Type for the traversal of package nodes.
103 (node-type
104 (name "package")
105 (description "the DAG of packages, excluding implicit inputs")
a773c314 106 (convert nodes-from-package)
88856916
LC
107
108 ;; We use package addresses as unique identifiers. This generally works
109 ;; well, but for generated package objects, we could end up with two
110 ;; packages that are not 'eq?', yet map to the same derivation (XXX).
111 (identifier (lift1 object-address %store-monad))
112 (label node-full-name)
113 (edges (lift1 package-node-edges %store-monad))))
114
115\f
b96a0640
LC
116;;;
117;;; Reverse package DAG.
118;;;
119
a53ecfc8
LC
120(define (all-packages) ;XXX: duplicated from (guix scripts refresh)
121 "Return the list of all the distro's packages."
122 (fold-packages (lambda (package result)
123 ;; Ignore deprecated packages.
124 (if (package-superseded package)
125 result
126 (cons package result)))
127 '()
128 #:select? (const #t))) ;include hidden packages
129
b96a0640
LC
130(define %reverse-package-node-type
131 ;; For this node type we first need to compute the list of packages and the
132 ;; list of back-edges. Since we want to do it only once, we use the
133 ;; promises below.
a53ecfc8 134 (let* ((packages (delay (all-packages)))
b96a0640
LC
135 (back-edges (delay (run-with-store #f ;store not actually needed
136 (node-back-edges %package-node-type
137 (force packages))))))
138 (node-type
139 (inherit %package-node-type)
140 (name "reverse-package")
141 (description "the reverse DAG of packages")
142 (edges (lift1 (force back-edges) %store-monad)))))
143
144\f
88856916
LC
145;;;
146;;; Package DAG using bags.
147;;;
148
149(define (bag-node-identifier thing)
150 "Return a unique identifier for THING, which may be a package, origin, or a
151file name."
152 ;; If THING is a file name (a string), we just return it; if it's a package
153 ;; or origin, we return its address. That gives us the object graph, but
154 ;; that may differ from the derivation graph (for instance,
155 ;; 'package-with-bootstrap-guile' generates fresh package objects, and
156 ;; several packages that are not 'eq?' may actually map to the same
157 ;; derivation.) Thus, we lower THING and use its derivation file name as a
158 ;; unique identifier.
159 (with-monad %store-monad
160 (if (string? thing)
161 (return thing)
162 (mlet %store-monad ((low (lower-object thing)))
163 (return (if (derivation? low)
164 (derivation-file-name low)
165 low))))))
166
167(define (bag-node-edges thing)
38b92daa
LC
168 "Return the list of dependencies of THING, a package or origin.
169Dependencies may include packages, origin, and file names."
170 (cond ((package? thing)
171 (match (bag-direct-inputs (package->bag thing))
172 (((labels things . outputs) ...)
173 things)))
174 ((origin? thing)
51385362 175 (cons (or (origin-patch-guile thing) (default-guile))
38b92daa
LC
176 (if (or (pair? (origin-patches thing))
177 (origin-snippet thing))
178 (match (origin-patch-inputs thing)
179 (#f '())
180 (((labels dependencies _ ...) ...)
181 (delete-duplicates dependencies eq?)))
182 '())))
183 (else
184 '())))
88856916
LC
185
186(define %bag-node-type
187 ;; Type for the traversal of package nodes via the "bag" representation,
188 ;; which includes implicit inputs.
189 (node-type
190 (name "bag")
191 (description "the DAG of packages, including implicit inputs")
a773c314 192 (convert nodes-from-package)
88856916
LC
193 (identifier bag-node-identifier)
194 (label node-full-name)
38b92daa
LC
195 (edges (lift1 (compose (cut filter package? <>) bag-node-edges)
196 %store-monad))))
197
198(define %bag-with-origins-node-type
199 (node-type
200 (name "bag-with-origins")
201 (description "the DAG of packages and origins, including implicit inputs")
a773c314 202 (convert nodes-from-package)
38b92daa
LC
203 (identifier bag-node-identifier)
204 (label node-full-name)
205 (edges (lift1 (lambda (thing)
206 (filter (match-lambda
207 ((? package?) #t)
208 ((? origin?) #t)
209 (_ #f))
210 (bag-node-edges thing)))
211 %store-monad))))
88856916
LC
212
213(define standard-package-set
55b2d921
LC
214 (mlambda ()
215 "Return the set of standard packages provided by GNU-BUILD-SYSTEM."
216 (match (standard-packages)
217 (((labels packages . output) ...)
218 (list->setq packages)))))
88856916
LC
219
220(define (bag-node-edges-sans-bootstrap thing)
221 "Like 'bag-node-edges', but pretend that the standard packages of
222GNU-BUILD-SYSTEM have zero dependencies."
223 (if (set-contains? (standard-package-set) thing)
224 '()
225 (bag-node-edges thing)))
226
227(define %bag-emerged-node-type
228 ;; Like %BAG-NODE-TYPE, but without the bootstrap subset of the DAG.
229 (node-type
230 (name "bag-emerged")
231 (description "same as 'bag', but without the bootstrap nodes")
a773c314 232 (convert nodes-from-package)
88856916
LC
233 (identifier bag-node-identifier)
234 (label node-full-name)
f88282af
LC
235 (edges (lift1 (compose (cut filter package? <>)
236 bag-node-edges-sans-bootstrap)
237 %store-monad))))
88856916 238
2b81eac0
LC
239(define %reverse-bag-node-type
240 ;; Type for the reverse traversal of package nodes via the "bag"
241 ;; representation, which includes implicit inputs.
a53ecfc8 242 (let* ((packages (delay (package-closure (all-packages))))
2b81eac0
LC
243 (back-edges (delay (run-with-store #f ;store not actually needed
244 (node-back-edges %bag-node-type
245 (force packages))))))
246 (node-type
247 (name "reverse-bag")
248 (description "the reverse DAG of packages, including implicit inputs")
249 (convert nodes-from-package)
250 (identifier bag-node-identifier)
251 (label node-full-name)
252 (edges (lift1 (force back-edges) %store-monad)))))
253
88856916
LC
254\f
255;;;
256;;; Derivation DAG.
257;;;
258
88856916
LC
259(define (derivation-dependencies obj)
260 "Return the <derivation> objects and store items corresponding to the
261dependencies of OBJ, a <derivation> or store item."
262 (if (derivation? obj)
a2500619 263 (append (map derivation-input-derivation (derivation-inputs obj))
88856916
LC
264 (derivation-sources obj))
265 '()))
266
267(define (derivation-node-identifier node)
268 "Return a unique identifier for NODE, which may be either a <derivation> or
269a plain store file."
270 (if (derivation? node)
271 (derivation-file-name node)
272 node))
273
274(define (derivation-node-label node)
275 "Return a label for NODE, a <derivation> object or plain store item."
276 (store-path-package-name (match node
277 ((? derivation? drv)
278 (derivation-file-name drv))
279 ((? string? file)
280 file))))
281
282(define %derivation-node-type
283 ;; DAG of derivations. Very accurate, very detailed, but usually too much
284 ;; detailed.
285 (node-type
286 (name "derivation")
287 (description "the DAG of derivations")
a773c314
LC
288 (convert (match-lambda
289 ((? package? package)
290 (with-monad %store-monad
291 (>>= (package->derivation package)
292 (lift1 list %store-monad))))
293 ((? derivation-path? item)
294 (mbegin %store-monad
295 ((store-lift add-temp-root) item)
015f17e8 296 (return (list (read-derivation-from-file item)))))
a773c314
LC
297 (x
298 (raise
299 (condition (&message (message "unsupported argument for \
300derivation graph")))))))
88856916
LC
301 (identifier (lift1 derivation-node-identifier %store-monad))
302 (label derivation-node-label)
303 (edges (lift1 derivation-dependencies %store-monad))))
304
305\f
306;;;
307;;; DAG of residual references (aka. run-time dependencies).
308;;;
309
72402021
LC
310(define intern
311 (mlambda (str)
312 "Intern STR, a string denoting a store item."
313 ;; This is necessary for %REFERENCE-NODE-TYPE and %REFERRER-NODE-TYPE
314 ;; because their nodes are strings but the (guix graph) traversal
315 ;; procedures expect to be able to compare nodes with 'eq?'.
316 str))
317
7f8fec0f
LC
318(define ensure-store-items
319 ;; Return a list of store items as a monadic value based on the given
320 ;; argument, which may be a store item or a package.
321 (match-lambda
322 ((? package? package)
323 ;; Return the output file names of PACKAGE.
324 (mlet %store-monad ((drv (package->derivation package)))
325 (return (match (derivation->output-paths drv)
326 (((_ . file-names) ...)
72402021 327 (map intern file-names))))))
7f8fec0f
LC
328 ((? store-path? item)
329 (with-monad %store-monad
72402021 330 (return (list (intern item)))))
7f8fec0f
LC
331 (x
332 (raise
333 (condition (&message (message "unsupported argument for \
334this type of graph")))))))
335
88856916
LC
336(define (references* item)
337 "Return as a monadic value the references of ITEM, based either on the
338information available in the local store or using information about
339substitutes."
340 (lambda (store)
f9e8a123 341 (guard (c ((store-protocol-error? c)
88856916
LC
342 (match (substitutable-path-info store (list item))
343 ((info)
72402021
LC
344 (values (map intern (substitutable-references info))
345 store))
88856916 346 (()
69daee23 347 (leave (G_ "references for '~a' are not known~%")
88856916 348 item)))))
72402021 349 (values (map intern (references store item)) store))))
88856916
LC
350
351(define %reference-node-type
352 (node-type
353 (name "references")
354 (description "the DAG of run-time dependencies (store references)")
7f8fec0f 355 (convert ensure-store-items)
72402021 356 (identifier (lift1 intern %store-monad))
88856916
LC
357 (label store-path-package-name)
358 (edges references*)))
359
7f8fec0f
LC
360(define non-derivation-referrers
361 (let ((referrers (store-lift referrers)))
362 (lambda (item)
363 "Return the referrers of ITEM, except '.drv' files."
364 (mlet %store-monad ((items (referrers item)))
72402021 365 (return (map intern (remove derivation-path? items)))))))
7f8fec0f
LC
366
367(define %referrer-node-type
368 (node-type
369 (name "referrers")
370 (description "the DAG of referrers in the store")
371 (convert ensure-store-items)
72402021 372 (identifier (lift1 intern %store-monad))
7f8fec0f
LC
373 (label store-path-package-name)
374 (edges non-derivation-referrers)))
375
88856916 376\f
b06a70e0
LC
377;;;
378;;; Scheme modules.
379;;;
380
381(define (module-from-package package)
382 (file-name->module-name (location-file (package-location package))))
383
384(define (source-module-dependencies* module)
385 "Like 'source-module-dependencies' but filter out modules that are not
386package modules, while attempting to retain user package modules."
387 (remove (match-lambda
388 (('guix _ ...) #t)
389 (('system _ ...) #t)
390 (('language _ ...) #t)
391 (('ice-9 _ ...) #t)
392 (('srfi _ ...) #t)
393 (_ #f))
394 (source-module-dependencies module)))
395
396(define %module-node-type
397 ;; Show the graph of package modules.
398 (node-type
399 (name "module")
400 (description "the graph of package modules")
401 (convert (lift1 (compose list module-from-package) %store-monad))
402 (identifier (lift1 identity %store-monad))
403 (label object->string)
404 (edges (lift1 source-module-dependencies* %store-monad))))
405
406\f
88856916
LC
407;;;
408;;; List of node types.
409;;;
410
411(define %node-types
412 ;; List of all the node types.
413 (list %package-node-type
b96a0640 414 %reverse-package-node-type
88856916 415 %bag-node-type
38b92daa 416 %bag-with-origins-node-type
88856916 417 %bag-emerged-node-type
2b81eac0 418 %reverse-bag-node-type
88856916 419 %derivation-node-type
7f8fec0f 420 %reference-node-type
b06a70e0
LC
421 %referrer-node-type
422 %module-node-type))
88856916
LC
423
424(define (lookup-node-type name)
425 "Return the node type called NAME. Raise an error if it is not found."
426 (or (find (lambda (type)
427 (string=? (node-type-name type) name))
428 %node-types)
69daee23 429 (leave (G_ "~a: unknown node type~%") name)))
88856916 430
642339dc
RW
431(define (lookup-backend name)
432 "Return the graph backend called NAME. Raise an error if it is not found."
433 (or (find (lambda (backend)
434 (string=? (graph-backend-name backend) name))
435 %graph-backends)
69daee23 436 (leave (G_ "~a: unknown backend~%") name)))
642339dc 437
88856916
LC
438(define (list-node-types)
439 "Print the available node types along with their synopsis."
69daee23 440 (display (G_ "The available node types are:\n"))
88856916
LC
441 (newline)
442 (for-each (lambda (type)
443 (format #t " - ~a: ~a~%"
444 (node-type-name type)
445 (node-type-description type)))
446 %node-types))
447
642339dc
RW
448(define (list-backends)
449 "Print the available backends along with their synopsis."
69daee23 450 (display (G_ "The available backend types are:\n"))
642339dc
RW
451 (newline)
452 (for-each (lambda (backend)
453 (format #t " - ~a: ~a~%"
454 (graph-backend-name backend)
455 (graph-backend-description backend)))
456 %graph-backends))
457
88856916 458\f
88a96c56
LC
459;;;
460;;; Displaying a path.
461;;;
462
463(define (display-path node1 node2 type)
464 "Display the shortest path from NODE1 to NODE2, of TYPE."
465 (mlet %store-monad ((path (shortest-path node1 node2 type)))
466 (define node-label
467 (let ((label (node-type-label type)))
468 ;; Special-case derivations and store items to print them in full,
469 ;; contrary to what their 'node-type-label' normally does.
470 (match-lambda
471 ((? derivation? drv) (derivation-file-name drv))
472 ((? string? str) str)
473 (node (label node)))))
474
475 (if path
476 (format #t "~{~a~%~}" (map node-label path))
477 (leave (G_ "no path from '~a' to '~a'~%")
478 (node-label node1) (node-label node2)))
479 (return #t)))
480
481\f
88856916
LC
482;;;
483;;; Command-line options.
484;;;
485
486(define %options
3e962e59
LC
487 (cons* (option '(#\t "type") #t #f
488 (lambda (opt name arg result)
489 (alist-cons 'node-type (lookup-node-type arg)
490 result)))
88a96c56
LC
491 (option '("path") #f #f
492 (lambda (opt name arg result)
493 (alist-cons 'path? #t result)))
3e962e59
LC
494 (option '("list-types") #f #f
495 (lambda (opt name arg result)
496 (list-node-types)
497 (exit 0)))
498 (option '(#\b "backend") #t #f
499 (lambda (opt name arg result)
500 (alist-cons 'backend (lookup-backend arg)
501 result)))
502 (option '("list-backends") #f #f
503 (lambda (opt name arg result)
504 (list-backends)
505 (exit 0)))
506 (option '(#\e "expression") #t #f
507 (lambda (opt name arg result)
508 (alist-cons 'expression arg result)))
509 (option '(#\s "system") #t #f
510 (lambda (opt name arg result)
511 (alist-cons 'system arg
512 (alist-delete 'system result eq?))))
ee9a735b
PN
513 (find (lambda (option)
514 (member "load-path" (option-names option)))
515 %standard-build-options)
3e962e59
LC
516 (option '(#\h "help") #f #f
517 (lambda args
518 (show-help)
519 (exit 0)))
520 (option '(#\V "version") #f #f
521 (lambda args
522 (show-version-and-exit "guix graph")))
523
524 %transformation-options))
88856916
LC
525
526(define (show-help)
527 ;; TRANSLATORS: Here 'dot' is the name of a program; it must not be
528 ;; translated.
69daee23 529 (display (G_ "Usage: guix graph PACKAGE...
34b1f339 530Emit a representation of the dependency graph of PACKAGE...\n"))
69daee23 531 (display (G_ "
642339dc 532 -b, --backend=TYPE produce a graph with the given backend TYPE"))
69daee23 533 (display (G_ "
642339dc 534 --list-backends list the available graph backends"))
69daee23 535 (display (G_ "
88856916 536 -t, --type=TYPE represent nodes of the given TYPE"))
69daee23 537 (display (G_ "
88856916 538 --list-types list the available graph types"))
88a96c56
LC
539 (display (G_ "
540 --path display the shortest path between the given nodes"))
69daee23 541 (display (G_ "
4c8f997a 542 -e, --expression=EXPR consider the package EXPR evaluates to"))
ebbfc59c
LC
543 (display (G_ "
544 -s, --system=SYSTEM consider the graph for SYSTEM--e.g., \"i686-linux\""))
88856916 545 (newline)
ee9a735b
PN
546 (display (G_ "
547 -L, --load-path=DIR prepend DIR to the package module search path"))
548 (newline)
3e962e59
LC
549 (show-transformation-options-help)
550 (newline)
69daee23 551 (display (G_ "
88856916 552 -h, --help display this help and exit"))
69daee23 553 (display (G_ "
88856916
LC
554 -V, --version display version information and exit"))
555 (newline)
556 (show-bug-report-information))
557
558(define %default-options
642339dc 559 `((node-type . ,%package-node-type)
ebbfc59c
LC
560 (backend . ,%graphviz-backend)
561 (system . ,(%current-system))))
88856916
LC
562
563\f
564;;;
565;;; Entry point.
566;;;
567
3794ce93
LC
568(define-command (guix-graph . args)
569 (category packaging)
570 (synopsis "view and query package dependency graphs")
571
88856916 572 (with-error-handling
3e962e59
LC
573 (define opts
574 (parse-command-line args %options
575 (list %default-options)
576 #:build-options? #f))
577 (define backend
578 (assoc-ref opts 'backend))
579 (define type
580 (assoc-ref opts 'node-type))
581
582 (with-store store
583 (let* ((transform (options->transformation opts))
584 (items (filter-map (match-lambda
585 (('argument . (? store-path? item))
586 item)
587 (('argument . spec)
588 (transform store
589 (specification->package spec)))
590 (('expression . exp)
591 (transform store
592 (read/eval-package-expression exp)))
593 (_ #f))
594 opts)))
09238d61
LC
595 (run-with-store store
596 ;; XXX: Since grafting can trigger unsolicited builds, disable it.
597 (mlet %store-monad ((_ (set-grafting #f))
598 (nodes (mapm %store-monad
599 (node-type-convert type)
88a96c56
LC
600 (reverse items))))
601 (if (assoc-ref opts 'path?)
602 (match nodes
603 (((node1 _ ...) (node2 _ ...))
604 (display-path node1 node2 type))
605 (_
606 (leave (G_ "'--path' option requires exactly two \
607nodes (given ~a)~%")
608 (length nodes))))
609 (export-graph (concatenate nodes)
610 (current-output-port)
611 #:node-type type
612 #:backend backend)))
09238d61 613 #:system (assq-ref opts 'system)))))
88856916
LC
614 #t)
615
616;;; graph.scm ends here