~ruther/guix-local

88a96c568c47c97d05d883ada5afbc4e1200b10f — Ludovic Courtès 6 years ago 36c2192
guix graph: Add '--path'.

* guix/scripts/graph.scm (display-path): New procedure.
(%options, show-help): Add '--path'.
(guix-graph): Handle it.
* tests/guix-graph.sh: Add tests.
* doc/guix.texi (Invoking guix graph): Document it.
(Invoking guix size): Mention it.
3 files changed, 102 insertions(+), 8 deletions(-)

M doc/guix.texi
M guix/scripts/graph.scm
M tests/guix-graph.sh
M doc/guix.texi => doc/guix.texi +46 -2
@@ 10021,6 10021,12 @@ In this example we see that the combination of the four packages takes
102.3@tie{}MiB in total, which is much less than the sum of each closure
since they have a lot of dependencies in common.

When looking at the profile returned by @command{guix size}, you may
find yourself wondering why a given package shows up in the profile at
all.  To understand it, you can use @command{guix graph --path -t
references} to display the shortest path between the two packages
(@pxref{Invoking guix graph}).

The available options are:

@table @option


@@ 10081,8 10087,9 @@ directly to the @command{dot} command of Graphviz.  It can also emit an
HTML page with embedded JavaScript code to display a ``chord diagram''
in a Web browser, using the @uref{https://d3js.org/, d3.js} library, or
emit Cypher queries to construct a graph in a graph database supporting
the @uref{https://www.opencypher.org/, openCypher} query language.
The general syntax is:
the @uref{https://www.opencypher.org/, openCypher} query language.  With
@option{--path}, it simply displays the shortest path between two
packages.  The general syntax is:

@example
guix graph @var{options} @var{package}@dots{}


@@ 10235,6 10242,29 @@ collected.

@end table

@cindex shortest path, between packages
Often, the graph of the package you are interested in does not fit on
your screen, and anyway all you want to know is @emph{why} that package
actually depends on some seemingly unrelated package.  The
@option{--path} option instructs @command{guix graph} to display the
shortest path between two packages (or derivations, or store items,
etc.):

@example
$ guix graph --path emacs libunistring
emacs@@26.3
mailutils@@3.9
libunistring@@0.9.10
$ guix graph --path -t derivation emacs libunistring
/gnu/store/@dots{}-emacs-26.3.drv
/gnu/store/@dots{}-mailutils-3.9.drv
/gnu/store/@dots{}-libunistring-0.9.10.drv
$ guix graph --path -t references emacs libunistring
/gnu/store/@dots{}-emacs-26.3
/gnu/store/@dots{}-libidn2-2.2.0
/gnu/store/@dots{}-libunistring-0.9.10
@end example

The available options are the following:

@table @option


@@ 10255,6 10285,20 @@ List the supported graph backends.

Currently, the available backends are Graphviz and d3.js.

@item --path
Display the shortest path between two nodes of the type specified by
@option{--type}.  The example below shows the shortest path between
@code{libreoffice} and @code{llvm} according to the references of
@code{libreoffice}:

@example
$ guix graph --path -t references libreoffice llvm
/gnu/store/@dots{}-libreoffice-6.4.2.2
/gnu/store/@dots{}-libepoxy-1.5.4
/gnu/store/@dots{}-mesa-19.3.4
/gnu/store/@dots{}-llvm-9.0.1
@end example

@item --expression=@var{expr}
@itemx -e @var{expr}
Consider the package @var{expr} evaluates to.

M guix/scripts/graph.scm => guix/scripts/graph.scm +41 -5
@@ 457,6 457,29 @@ package modules, while attempting to retain user package modules."


;;;
;;; Displaying a path.
;;;

(define (display-path node1 node2 type)
  "Display the shortest path from NODE1 to NODE2, of TYPE."
  (mlet %store-monad ((path (shortest-path node1 node2 type)))
    (define node-label
      (let ((label (node-type-label type)))
        ;; Special-case derivations and store items to print them in full,
        ;; contrary to what their 'node-type-label' normally does.
        (match-lambda
          ((? derivation? drv) (derivation-file-name drv))
          ((? string? str) str)
          (node (label node)))))

    (if path
        (format #t "~{~a~%~}" (map node-label path))
        (leave (G_ "no path from '~a' to '~a'~%")
               (node-label node1) (node-label node2)))
    (return #t)))


;;;
;;; Command-line options.
;;;



@@ 465,6 488,9 @@ package modules, while attempting to retain user package modules."
                 (lambda (opt name arg result)
                   (alist-cons 'node-type (lookup-node-type arg)
                               result)))
         (option '("path") #f #f
                 (lambda (opt name arg result)
                   (alist-cons 'path? #t result)))
         (option '("list-types") #f #f
                 (lambda (opt name arg result)
                   (list-node-types)


@@ 511,6 537,8 @@ Emit a representation of the dependency graph of PACKAGE...\n"))
  (display (G_ "
      --list-types       list the available graph types"))
  (display (G_ "
      --path             display the shortest path between the given nodes"))
  (display (G_ "
  -e, --expression=EXPR  consider the package EXPR evaluates to"))
  (display (G_ "
  -s, --system=SYSTEM    consider the graph for SYSTEM--e.g., \"i686-linux\""))


@@ 566,11 594,19 @@ Emit a representation of the dependency graph of PACKAGE...\n"))
          (mlet %store-monad ((_     (set-grafting #f))
                              (nodes (mapm %store-monad
                                           (node-type-convert type)
                                           items)))
            (export-graph (concatenate nodes)
                          (current-output-port)
                          #:node-type type
                          #:backend backend))
                                           (reverse items))))
            (if (assoc-ref opts 'path?)
                (match nodes
                  (((node1 _ ...) (node2 _ ...))
                   (display-path node1 node2 type))
                  (_
                   (leave (G_ "'--path' option requires exactly two \
nodes (given ~a)~%")
                          (length nodes))))
                (export-graph (concatenate nodes)
                              (current-output-port)
                              #:node-type type
                              #:backend backend)))
          #:system (assq-ref opts 'system)))))
  #t)


M tests/guix-graph.sh => tests/guix-graph.sh +15 -1
@@ 1,5 1,5 @@
# GNU Guix --- Functional package management for GNU
# Copyright © 2015, 2016, 2019 Ludovic Courtès <ludo@gnu.org>
# Copyright © 2015, 2016, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
# Copyright © 2019 Simon Tournier <zimon.toutoune@gmail.com>
#
# This file is part of GNU Guix.


@@ 82,3 82,17 @@ then false; else true; fi

# Try --load-path
guix graph -L $module_dir dummy | grep 'label = "dummy'

# Displaying shortest paths (or lack thereof).
if guix graph --path emacs vim; then false; else true; fi

path="\
emacs
gnutls
guile
libffi"
test "`guix graph --path emacs libffi | cut -d '@' -f1`" = "$path"

# At the derivation level, there's a direct path because libffi is propagated
# via gtk+.
test "`guix graph --path -t derivation emacs libffi | wc -l`" -ge 2