guix-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

03/03: Add 'guix graph'.


From: Ludovic Courtès
Subject: 03/03: Add 'guix graph'.
Date: Wed, 26 Aug 2015 22:49:34 +0000

civodul pushed a commit to branch master
in repository guix.

commit 888569161c0cb55a2700806aded7128cfe605857
Author: Ludovic Courtès <address@hidden>
Date:   Thu Aug 27 00:36:41 2015 +0200

    Add 'guix graph'.
    
    * guix/scripts/graph.scm, tests/graph.scm, tests/guix-graph.sh,
      doc/images/coreutils-bag-graph.dot, doc/images/coreutils-graph.dot: New
      files.
    * Makefile.am (MODULES): Add guix/scripts/graph.scm.
      (SH_TESTS): Add tests/guix-graph.sh.
      (SCM_TESTS): Add tests/graph.scm.
    * doc.am (DOT_FILES, DOT_VECTOR_GRAPHICS): New variables.
      (EXTRA_DIST): Use them.
      (dist_infoimage_DATA): Use $(DOT_FILES).
      (pdf-local, info-local, ps-local): Likewise.
    * doc/guix.texi (Packages with Multiple Outputs): Add cross-reference to 
'guix
      graph'.
      (Invoking guix gc): Likewise.
      (Invoking guix graph): New section.
---
 .gitignore                         |    4 +
 Makefile.am                        |    3 +
 doc.am                             |   23 ++-
 doc/guix.texi                      |  106 +++++++++-
 doc/images/coreutils-bag-graph.dot |  213 ++++++++++++++++++
 doc/images/coreutils-graph.dot     |   23 ++
 guix/scripts/graph.scm             |  426 ++++++++++++++++++++++++++++++++++++
 tests/graph.scm                    |  193 ++++++++++++++++
 tests/guix-graph.sh                |   34 +++
 9 files changed, 1017 insertions(+), 8 deletions(-)

diff --git a/.gitignore b/.gitignore
index c7a6cdd..4547557 100644
--- a/.gitignore
+++ b/.gitignore
@@ -126,3 +126,7 @@ GTAGS
 /doc/os-config-desktop.texi
 /doc/*.1
 /etc/guix-daemon.service
+/doc/images/bootstrap-graph.pdf
+/doc/images/coreutils-bag-graph.png
+/doc/images/coreutils-graph.png
+/doc/images/coreutils-size-map.eps
diff --git a/Makefile.am b/Makefile.am
index 76d505a..c324649 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -120,6 +120,7 @@ MODULES =                                   \
   guix/scripts/publish.scm                     \
   guix/scripts/edit.scm                                \
   guix/scripts/size.scm                                \
+  guix/scripts/graph.scm                       \
   guix.scm                                     \
   $(GNU_SYSTEM_MODULES)
 
@@ -209,6 +210,7 @@ SCM_TESTS =                                 \
   tests/lint.scm                               \
   tests/publish.scm                            \
   tests/size.scm                               \
+  tests/graph.scm                              \
   tests/file-systems.scm                       \
   tests/containers.scm
 
@@ -238,6 +240,7 @@ SH_TESTS =                                  \
   tests/guix-archive.sh                                \
   tests/guix-authenticate.sh                   \
   tests/guix-environment.sh                    \
+  tests/guix-graph.sh                          \
   tests/guix-lint.sh
 
 if BUILD_DAEMON
diff --git a/doc.am b/doc.am
index 1f0b832..9c1dc83 100644
--- a/doc.am
+++ b/doc.am
@@ -18,13 +18,22 @@
 # along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
 
 info_TEXINFOS = doc/guix.texi
+
+DOT_FILES =                                    \
+  doc/images/bootstrap-graph.dot               \
+  doc/images/coreutils-graph.dot               \
+  doc/images/coreutils-bag-graph.dot
+
+DOT_VECTOR_GRAPHICS =                          \
+  $(DOT_FILES:%.dot=%.eps)                     \
+  $(DOT_FILES:%.dot=%.pdf)
+
 EXTRA_DIST +=                                  \
   doc/contributing.texi                                \
   doc/emacs.texi                               \
   doc/fdl-1.3.texi                             \
-  doc/images/bootstrap-graph.dot               \
-  doc/images/bootstrap-graph.eps               \
-  doc/images/bootstrap-graph.pdf               \
+  $(DOT_FILES)                                 \
+  $(DOT_VECTOR_GRAPHICS)                       \
   doc/images/coreutils-size-map.eps            \
   doc/environment-gdb.scm                      \
   doc/package-hello.scm
@@ -44,7 +53,7 @@ doc/os-config-%.texi: gnu/system/examples/%.tmpl
 
 infoimagedir = $(infodir)/images
 dist_infoimage_DATA =                          \
-  doc/images/bootstrap-graph.png               \
+  $(DOT_FILES:%.dot=%.png)                     \
   doc/images/coreutils-size-map.png
 
 # Try hard to obtain an image size and aspect that's reasonable for inclusion
@@ -72,9 +81,9 @@ DOT_OPTIONS =                                 \
 # We cannot add new dependencies to `doc/guix.pdf' & co. (info "(automake)
 # Extending").  Using the `-local' rules is imperfect, because they may be
 # triggered after the main rule.  Oh, well.
-pdf-local: $(top_srcdir)/doc/images/bootstrap-graph.pdf
-info-local: $(top_srcdir)/doc/images/bootstrap-graph.png
-ps-local: $(top_srcdir)/doc/images/bootstrap-graph.eps         \
+pdf-local: $(DOT_FILES=%.dot=$(top_srcdir)/%.pdf)
+info-local: $(DOT_FILES=%.dot=$(top_srcdir)/%.png)
+ps-local: $(DOT_FILES=%.dot=$(top_srcdir)/%.eps)               \
          $(top_srcdir)/doc/images/coreutils-size-map.eps
 dvi-local: ps-local
 
diff --git a/doc/guix.texi b/doc/guix.texi
index 39093a9..6ea143f 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -132,6 +132,7 @@ Utilities
 * Invoking guix refresh::       Updating package definitions.
 * Invoking guix lint::          Finding errors in package definitions.
 * Invoking guix size::          Profiling disk usage.
+* Invoking guix graph::         Visualizing the graph of packages.
 * Invoking guix environment::   Setting up development environments.
 * Invoking guix publish::       Sharing substitutes.
 
@@ -1604,6 +1605,7 @@ libraries.  In this case, we leave the command-line tools 
in the default
 output, whereas the GUIs are in a separate output.  This allows users
 who do not need the GUIs to save space.  The @command{guix size} command
 can help find out about such situations (@pxref{Invoking guix size}).
address@hidden graph} can also be helpful (@pxref{Invoking guix graph}).
 
 There are several such multiple-output packages in the GNU distribution.
 Other conventional output names include @code{lib} for libraries and
@@ -1690,7 +1692,8 @@ of these, recursively.  In other words, the returned list 
is the
 @dfn{transitive closure} of the store files.
 
 @xref{Invoking guix size}, for a tool to profile the size of an
-element's closure.
+element's closure.  @xref{Invoking guix graph}, for a tool to visualize
+the graph of references.
 
 @end table
 
@@ -3434,6 +3437,7 @@ programming interface of Guix in a convenient way.
 * Invoking guix refresh::       Updating package definitions.
 * Invoking guix lint::          Finding errors in package definitions.
 * Invoking guix size::          Profiling disk usage.
+* Invoking guix graph::         Visualizing the graph of packages.
 * Invoking guix environment::   Setting up development environments.
 * Invoking guix publish::       Sharing substitutes.
 @end menu
@@ -4263,6 +4267,106 @@ Consider packages for @var{system}---e.g., 
@code{x86_64-linux}.
 
 @end table
 
address@hidden Invoking guix graph
address@hidden Invoking @command{guix graph}
+
address@hidden DAG
+Packages and their dependencies form a @dfn{graph}, specifically a
+directed acyclic graph (DAG).  It can quickly become difficult to have a
+mental model of the package DAG, so the @command{guix graph} command is
+here to provide a visual representation of the DAG.  @command{guix
+graph} emits a DAG representation in the input format of
address@hidden://www.graphviz.org/, Graphviz}, so its output can be passed
+directly to Graphviz's @command{dot} command, for instance.  The general
+syntax is:
+
address@hidden
+guix graph @var{options} @address@hidden
address@hidden example
+
+For example, the following command generates a PDF file representing the
+package DAG for the address@hidden Utilities, showing its build-time
+dependencies:
+
address@hidden
+guix graph coreutils | dot -Tpdf > dag.pdf
address@hidden example
+
+The output looks like this:
+
address@hidden/coreutils-graph,2in,,Dependency graph of the GNU Coreutils}
+
+Nice little graph, no?
+
+But there's more than one graph!  The one above is concise: it's the
+graph of package objects, omitting implicit inputs such as GCC, libc,
+grep, etc.  It's often useful to have such a concise graph, but
+sometimes you want to see more details.  @command{guix graph} supports
+several types of graphs, allowing you to choose the level of details:
+
address@hidden @code
address@hidden package
+This is the default type, the one we used above.  It shows the DAG of
+package objects, excluding implicit dependencies.  It is concise, but
+filters out many details.
+
address@hidden bag-emerged
+This is the package DAG, @emph{including} implicit inputs.
+
+For instance, the following command:
+
address@hidden
+guix graph --type=bag-emerged coreutils | dot -Tpdf > dag.pdf
address@hidden example
+
+... yields this bigger graph:
+
address@hidden/coreutils-bag-graph,,5in,Detailed dependency graph of the GNU 
Coreutils}
+
+At the bottom of the graph, we see all the implicit inputs of
address@hidden (@pxref{Build Systems, @code{gnu-build-system}}).
+
+Now, note that the dependencies of those implicit inputs---that is, the
address@hidden dependencies} (@pxref{Bootstrapping})---are not shown
+here, for conciseness.
+
address@hidden bag
+Similar to @code{bag-emerged}, but this time including all the bootstrap
+dependencies.
+
address@hidden derivations
+This is the most detailed representation: It shows the DAG of
+derivations (@pxref{Derivations}) and plain store items.  Compared to
+the above representation, many additional nodes are visible, including
+builds scripts, patches, Guile modules, etc.
+
address@hidden table
+
+All the above types correspond to @emph{build-time dependencies}.  The
+following graph type represents the @emph{run-time dependencies}:
+
address@hidden @code
address@hidden references
+This is the graph of @dfn{references} of a package output, as returned
+by @command{guix gc --references} (@pxref{Invoking guix gc}).
+
+If the given package output is not available in the store, @command{guix
+graph} attempts to obtain dependency information from substitutes.
address@hidden table
+
+The available options are the following:
+
address@hidden @option
address@hidden address@hidden
address@hidden -t @var{type}
+Produce a graph output of @var{type}, where @var{type} must be one of
+the values listed above.
+
address@hidden --list-types
+List the supported graph types.
address@hidden table
+
+
 @node Invoking guix environment
 @section Invoking @command{guix environment}
 
diff --git a/doc/images/coreutils-bag-graph.dot 
b/doc/images/coreutils-bag-graph.dot
new file mode 100644
index 0000000..c36268e
--- /dev/null
+++ b/doc/images/coreutils-bag-graph.dot
@@ -0,0 +1,213 @@
+digraph "Guix bag-emerged" {
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" [label = 
"coreutils-8.24", shape = box, fontname = Helvetica];
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" -> 
"/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" [color = red];
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" -> 
"/gnu/store/7176h825gaw745sdkwkgh7jip9w26w8m-tar-1.28.drv" [color = red];
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" -> 
"/gnu/store/86bbbaav5ilnr63cpihi9h03ila7cr1k-gzip-1.6.drv" [color = red];
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" -> 
"/gnu/store/ab82abh8l2dr56j0s9z68v6i872dic45-bzip2-1.0.6.drv" [color = red];
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" -> 
"/gnu/store/x9dji1ms9cj9vbl15bksci7w76zcwvkj-xz-5.0.4.drv" [color = red];
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" -> 
"/gnu/store/l8712bh52742kzav8w11n97l0vzim838-file-5.22.drv" [color = red];
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" -> 
"/gnu/store/52b3d4q4nyv0i0n1phnwms3xan38q8is-diffutils-3.3.drv" [color = red];
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" -> 
"/gnu/store/vaksjdasfzhbj6jzc46fdglwy75y8ggs-patch-2.7.5.drv" [color = red];
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" -> 
"/gnu/store/4dikg9vw3fnhvjx86wq5jlpaw58nfmbv-sed-4.2.2.drv" [color = red];
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" -> 
"/gnu/store/6q81294lvwzwqwj3s5flhl2fap5ac1qf-findutils-4.4.2.drv" [color = red];
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" -> 
"/gnu/store/0qzl2vj3fwz03m6a8v7yypqhb58b4408-gawk-4.1.3.drv" [color = red];
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" -> 
"/gnu/store/0w9md4s7vasgi5jy1xvxnsg4a3s134a9-grep-2.21.drv" [color = red];
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" -> 
"/gnu/store/ldxarpgn1f4xbghbw0vqibdbi5cv3snl-coreutils-8.24.drv" [color = red];
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" -> 
"/gnu/store/j7h01h99vrcs8wr0xx997wl2ijqhrz9b-make-4.1.drv" [color = red];
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" -> 
"/gnu/store/8ghhz8yi3m9d4s07r443106g4pffd8q2-bash-4.3.39.drv" [color = red];
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" -> 
"/gnu/store/sx1s4j6p0sm66znaaizb9a4cfwy109zy-ld-wrapper-0.drv" [color = red];
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" -> 
"/gnu/store/gj0jr0lz5siyn0ifq1vnksvfdcmd1gw7-binutils-2.25.drv" [color = red];
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" -> 
"/gnu/store/a74b2jvm3vv405a9wkd0c2v47s17alyi-gcc-4.9.3.drv" [color = red];
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" -> 
"/gnu/store/0b68fcwgd01v2mji5pyda5ag9amg7d10-glibc-2.21.drv" [color = red];
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" -> 
"/gnu/store/phkvp3cp8yqfqiv7i35j64lv671iv00x-glibc-utf8-locales-2.21.drv" 
[color = red];
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" -> 
"/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" [color = red];
+  "/gnu/store/rgbnfjyvx2i44x2iwi62jsk76rg5vfr0-coreutils-8.24.drv" -> 
"/gnu/store/1jzdjkign6jjsanmfhzs45kngi9gw5k8-gmp-6.0.0a.drv" [color = red];
+  "/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" [label = 
"perl-5.16.1", shape = box, fontname = Helvetica];
+  "/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" -> 
"/gnu/store/7176h825gaw745sdkwkgh7jip9w26w8m-tar-1.28.drv" [color = red];
+  "/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" -> 
"/gnu/store/86bbbaav5ilnr63cpihi9h03ila7cr1k-gzip-1.6.drv" [color = red];
+  "/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" -> 
"/gnu/store/ab82abh8l2dr56j0s9z68v6i872dic45-bzip2-1.0.6.drv" [color = red];
+  "/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" -> 
"/gnu/store/x9dji1ms9cj9vbl15bksci7w76zcwvkj-xz-5.0.4.drv" [color = red];
+  "/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" -> 
"/gnu/store/l8712bh52742kzav8w11n97l0vzim838-file-5.22.drv" [color = red];
+  "/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" -> 
"/gnu/store/52b3d4q4nyv0i0n1phnwms3xan38q8is-diffutils-3.3.drv" [color = red];
+  "/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" -> 
"/gnu/store/vaksjdasfzhbj6jzc46fdglwy75y8ggs-patch-2.7.5.drv" [color = red];
+  "/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" -> 
"/gnu/store/4dikg9vw3fnhvjx86wq5jlpaw58nfmbv-sed-4.2.2.drv" [color = red];
+  "/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" -> 
"/gnu/store/6q81294lvwzwqwj3s5flhl2fap5ac1qf-findutils-4.4.2.drv" [color = red];
+  "/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" -> 
"/gnu/store/0qzl2vj3fwz03m6a8v7yypqhb58b4408-gawk-4.1.3.drv" [color = red];
+  "/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" -> 
"/gnu/store/0w9md4s7vasgi5jy1xvxnsg4a3s134a9-grep-2.21.drv" [color = red];
+  "/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" -> 
"/gnu/store/ldxarpgn1f4xbghbw0vqibdbi5cv3snl-coreutils-8.24.drv" [color = red];
+  "/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" -> 
"/gnu/store/j7h01h99vrcs8wr0xx997wl2ijqhrz9b-make-4.1.drv" [color = red];
+  "/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" -> 
"/gnu/store/8ghhz8yi3m9d4s07r443106g4pffd8q2-bash-4.3.39.drv" [color = red];
+  "/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" -> 
"/gnu/store/sx1s4j6p0sm66znaaizb9a4cfwy109zy-ld-wrapper-0.drv" [color = red];
+  "/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" -> 
"/gnu/store/gj0jr0lz5siyn0ifq1vnksvfdcmd1gw7-binutils-2.25.drv" [color = red];
+  "/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" -> 
"/gnu/store/a74b2jvm3vv405a9wkd0c2v47s17alyi-gcc-4.9.3.drv" [color = red];
+  "/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" -> 
"/gnu/store/0b68fcwgd01v2mji5pyda5ag9amg7d10-glibc-2.21.drv" [color = red];
+  "/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" -> 
"/gnu/store/phkvp3cp8yqfqiv7i35j64lv671iv00x-glibc-utf8-locales-2.21.drv" 
[color = red];
+  "/gnu/store/7176h825gaw745sdkwkgh7jip9w26w8m-tar-1.28.drv" [label = 
"tar-1.28", shape = box, fontname = Helvetica];
+  "/gnu/store/86bbbaav5ilnr63cpihi9h03ila7cr1k-gzip-1.6.drv" [label = 
"gzip-1.6", shape = box, fontname = Helvetica];
+  "/gnu/store/ab82abh8l2dr56j0s9z68v6i872dic45-bzip2-1.0.6.drv" [label = 
"bzip2-1.0.6", shape = box, fontname = Helvetica];
+  "/gnu/store/x9dji1ms9cj9vbl15bksci7w76zcwvkj-xz-5.0.4.drv" [label = 
"xz-5.0.4", shape = box, fontname = Helvetica];
+  "/gnu/store/l8712bh52742kzav8w11n97l0vzim838-file-5.22.drv" [label = 
"file-5.22", shape = box, fontname = Helvetica];
+  "/gnu/store/52b3d4q4nyv0i0n1phnwms3xan38q8is-diffutils-3.3.drv" [label = 
"diffutils-3.3", shape = box, fontname = Helvetica];
+  "/gnu/store/vaksjdasfzhbj6jzc46fdglwy75y8ggs-patch-2.7.5.drv" [label = 
"patch-2.7.5", shape = box, fontname = Helvetica];
+  "/gnu/store/4dikg9vw3fnhvjx86wq5jlpaw58nfmbv-sed-4.2.2.drv" [label = 
"sed-4.2.2", shape = box, fontname = Helvetica];
+  "/gnu/store/6q81294lvwzwqwj3s5flhl2fap5ac1qf-findutils-4.4.2.drv" [label = 
"findutils-4.4.2", shape = box, fontname = Helvetica];
+  "/gnu/store/0qzl2vj3fwz03m6a8v7yypqhb58b4408-gawk-4.1.3.drv" [label = 
"gawk-4.1.3", shape = box, fontname = Helvetica];
+  "/gnu/store/0w9md4s7vasgi5jy1xvxnsg4a3s134a9-grep-2.21.drv" [label = 
"grep-2.21", shape = box, fontname = Helvetica];
+  "/gnu/store/ldxarpgn1f4xbghbw0vqibdbi5cv3snl-coreutils-8.24.drv" [label = 
"coreutils-8.24", shape = box, fontname = Helvetica];
+  "/gnu/store/j7h01h99vrcs8wr0xx997wl2ijqhrz9b-make-4.1.drv" [label = 
"make-4.1", shape = box, fontname = Helvetica];
+  "/gnu/store/8ghhz8yi3m9d4s07r443106g4pffd8q2-bash-4.3.39.drv" [label = 
"bash-4.3.39", shape = box, fontname = Helvetica];
+  "/gnu/store/sx1s4j6p0sm66znaaizb9a4cfwy109zy-ld-wrapper-0.drv" [label = 
"ld-wrapper-0", shape = box, fontname = Helvetica];
+  "/gnu/store/gj0jr0lz5siyn0ifq1vnksvfdcmd1gw7-binutils-2.25.drv" [label = 
"binutils-2.25", shape = box, fontname = Helvetica];
+  "/gnu/store/a74b2jvm3vv405a9wkd0c2v47s17alyi-gcc-4.9.3.drv" [label = 
"gcc-4.9.3", shape = box, fontname = Helvetica];
+  "/gnu/store/0b68fcwgd01v2mji5pyda5ag9amg7d10-glibc-2.21.drv" [label = 
"glibc-2.21", shape = box, fontname = Helvetica];
+  "/gnu/store/phkvp3cp8yqfqiv7i35j64lv671iv00x-glibc-utf8-locales-2.21.drv" 
[label = "glibc-utf8-locales-2.21", shape = box, fontname = Helvetica];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" [label = 
"acl-2.2.52", shape = box, fontname = Helvetica];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" [color = red];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" [color = red];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/fi3hvhhi6jfm7qmlxayrd551d3wxp115-sed-4.2.2.drv" [color = red];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/7176h825gaw745sdkwkgh7jip9w26w8m-tar-1.28.drv" [color = red];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/86bbbaav5ilnr63cpihi9h03ila7cr1k-gzip-1.6.drv" [color = red];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/ab82abh8l2dr56j0s9z68v6i872dic45-bzip2-1.0.6.drv" [color = red];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/x9dji1ms9cj9vbl15bksci7w76zcwvkj-xz-5.0.4.drv" [color = red];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/l8712bh52742kzav8w11n97l0vzim838-file-5.22.drv" [color = red];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/52b3d4q4nyv0i0n1phnwms3xan38q8is-diffutils-3.3.drv" [color = red];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/vaksjdasfzhbj6jzc46fdglwy75y8ggs-patch-2.7.5.drv" [color = red];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/4dikg9vw3fnhvjx86wq5jlpaw58nfmbv-sed-4.2.2.drv" [color = red];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/6q81294lvwzwqwj3s5flhl2fap5ac1qf-findutils-4.4.2.drv" [color = red];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/0qzl2vj3fwz03m6a8v7yypqhb58b4408-gawk-4.1.3.drv" [color = red];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/0w9md4s7vasgi5jy1xvxnsg4a3s134a9-grep-2.21.drv" [color = red];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/ldxarpgn1f4xbghbw0vqibdbi5cv3snl-coreutils-8.24.drv" [color = red];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/j7h01h99vrcs8wr0xx997wl2ijqhrz9b-make-4.1.drv" [color = red];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/8ghhz8yi3m9d4s07r443106g4pffd8q2-bash-4.3.39.drv" [color = red];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/sx1s4j6p0sm66znaaizb9a4cfwy109zy-ld-wrapper-0.drv" [color = red];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/gj0jr0lz5siyn0ifq1vnksvfdcmd1gw7-binutils-2.25.drv" [color = red];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/a74b2jvm3vv405a9wkd0c2v47s17alyi-gcc-4.9.3.drv" [color = red];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/0b68fcwgd01v2mji5pyda5ag9amg7d10-glibc-2.21.drv" [color = red];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/phkvp3cp8yqfqiv7i35j64lv671iv00x-glibc-utf8-locales-2.21.drv" 
[color = red];
+  "/gnu/store/bx5ksjc77qy38p2y8xr5cph59kkh5aqc-acl-2.2.52.drv" -> 
"/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" [color = red];
+  "/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" [label = 
"gettext-0.19.5", shape = box, fontname = Helvetica];
+  "/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" -> 
"/gnu/store/7176h825gaw745sdkwkgh7jip9w26w8m-tar-1.28.drv" [color = red];
+  "/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" -> 
"/gnu/store/86bbbaav5ilnr63cpihi9h03ila7cr1k-gzip-1.6.drv" [color = red];
+  "/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" -> 
"/gnu/store/ab82abh8l2dr56j0s9z68v6i872dic45-bzip2-1.0.6.drv" [color = red];
+  "/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" -> 
"/gnu/store/x9dji1ms9cj9vbl15bksci7w76zcwvkj-xz-5.0.4.drv" [color = red];
+  "/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" -> 
"/gnu/store/l8712bh52742kzav8w11n97l0vzim838-file-5.22.drv" [color = red];
+  "/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" -> 
"/gnu/store/52b3d4q4nyv0i0n1phnwms3xan38q8is-diffutils-3.3.drv" [color = red];
+  "/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" -> 
"/gnu/store/vaksjdasfzhbj6jzc46fdglwy75y8ggs-patch-2.7.5.drv" [color = red];
+  "/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" -> 
"/gnu/store/4dikg9vw3fnhvjx86wq5jlpaw58nfmbv-sed-4.2.2.drv" [color = red];
+  "/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" -> 
"/gnu/store/6q81294lvwzwqwj3s5flhl2fap5ac1qf-findutils-4.4.2.drv" [color = red];
+  "/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" -> 
"/gnu/store/0qzl2vj3fwz03m6a8v7yypqhb58b4408-gawk-4.1.3.drv" [color = red];
+  "/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" -> 
"/gnu/store/0w9md4s7vasgi5jy1xvxnsg4a3s134a9-grep-2.21.drv" [color = red];
+  "/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" -> 
"/gnu/store/ldxarpgn1f4xbghbw0vqibdbi5cv3snl-coreutils-8.24.drv" [color = red];
+  "/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" -> 
"/gnu/store/j7h01h99vrcs8wr0xx997wl2ijqhrz9b-make-4.1.drv" [color = red];
+  "/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" -> 
"/gnu/store/8ghhz8yi3m9d4s07r443106g4pffd8q2-bash-4.3.39.drv" [color = red];
+  "/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" -> 
"/gnu/store/sx1s4j6p0sm66znaaizb9a4cfwy109zy-ld-wrapper-0.drv" [color = red];
+  "/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" -> 
"/gnu/store/gj0jr0lz5siyn0ifq1vnksvfdcmd1gw7-binutils-2.25.drv" [color = red];
+  "/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" -> 
"/gnu/store/a74b2jvm3vv405a9wkd0c2v47s17alyi-gcc-4.9.3.drv" [color = red];
+  "/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" -> 
"/gnu/store/0b68fcwgd01v2mji5pyda5ag9amg7d10-glibc-2.21.drv" [color = red];
+  "/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" -> 
"/gnu/store/phkvp3cp8yqfqiv7i35j64lv671iv00x-glibc-utf8-locales-2.21.drv" 
[color = red];
+  "/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" -> 
"/gnu/store/zv0yx4506q6k13vkl8k7pqnfirnlqplg-expat-2.1.0.drv" [color = red];
+  "/gnu/store/zv0yx4506q6k13vkl8k7pqnfirnlqplg-expat-2.1.0.drv" [label = 
"expat-2.1.0", shape = box, fontname = Helvetica];
+  "/gnu/store/zv0yx4506q6k13vkl8k7pqnfirnlqplg-expat-2.1.0.drv" -> 
"/gnu/store/7176h825gaw745sdkwkgh7jip9w26w8m-tar-1.28.drv" [color = red];
+  "/gnu/store/zv0yx4506q6k13vkl8k7pqnfirnlqplg-expat-2.1.0.drv" -> 
"/gnu/store/86bbbaav5ilnr63cpihi9h03ila7cr1k-gzip-1.6.drv" [color = red];
+  "/gnu/store/zv0yx4506q6k13vkl8k7pqnfirnlqplg-expat-2.1.0.drv" -> 
"/gnu/store/ab82abh8l2dr56j0s9z68v6i872dic45-bzip2-1.0.6.drv" [color = red];
+  "/gnu/store/zv0yx4506q6k13vkl8k7pqnfirnlqplg-expat-2.1.0.drv" -> 
"/gnu/store/x9dji1ms9cj9vbl15bksci7w76zcwvkj-xz-5.0.4.drv" [color = red];
+  "/gnu/store/zv0yx4506q6k13vkl8k7pqnfirnlqplg-expat-2.1.0.drv" -> 
"/gnu/store/l8712bh52742kzav8w11n97l0vzim838-file-5.22.drv" [color = red];
+  "/gnu/store/zv0yx4506q6k13vkl8k7pqnfirnlqplg-expat-2.1.0.drv" -> 
"/gnu/store/52b3d4q4nyv0i0n1phnwms3xan38q8is-diffutils-3.3.drv" [color = red];
+  "/gnu/store/zv0yx4506q6k13vkl8k7pqnfirnlqplg-expat-2.1.0.drv" -> 
"/gnu/store/vaksjdasfzhbj6jzc46fdglwy75y8ggs-patch-2.7.5.drv" [color = red];
+  "/gnu/store/zv0yx4506q6k13vkl8k7pqnfirnlqplg-expat-2.1.0.drv" -> 
"/gnu/store/4dikg9vw3fnhvjx86wq5jlpaw58nfmbv-sed-4.2.2.drv" [color = red];
+  "/gnu/store/zv0yx4506q6k13vkl8k7pqnfirnlqplg-expat-2.1.0.drv" -> 
"/gnu/store/6q81294lvwzwqwj3s5flhl2fap5ac1qf-findutils-4.4.2.drv" [color = red];
+  "/gnu/store/zv0yx4506q6k13vkl8k7pqnfirnlqplg-expat-2.1.0.drv" -> 
"/gnu/store/0qzl2vj3fwz03m6a8v7yypqhb58b4408-gawk-4.1.3.drv" [color = red];
+  "/gnu/store/zv0yx4506q6k13vkl8k7pqnfirnlqplg-expat-2.1.0.drv" -> 
"/gnu/store/0w9md4s7vasgi5jy1xvxnsg4a3s134a9-grep-2.21.drv" [color = red];
+  "/gnu/store/zv0yx4506q6k13vkl8k7pqnfirnlqplg-expat-2.1.0.drv" -> 
"/gnu/store/ldxarpgn1f4xbghbw0vqibdbi5cv3snl-coreutils-8.24.drv" [color = red];
+  "/gnu/store/zv0yx4506q6k13vkl8k7pqnfirnlqplg-expat-2.1.0.drv" -> 
"/gnu/store/j7h01h99vrcs8wr0xx997wl2ijqhrz9b-make-4.1.drv" [color = red];
+  "/gnu/store/zv0yx4506q6k13vkl8k7pqnfirnlqplg-expat-2.1.0.drv" -> 
"/gnu/store/8ghhz8yi3m9d4s07r443106g4pffd8q2-bash-4.3.39.drv" [color = red];
+  "/gnu/store/zv0yx4506q6k13vkl8k7pqnfirnlqplg-expat-2.1.0.drv" -> 
"/gnu/store/sx1s4j6p0sm66znaaizb9a4cfwy109zy-ld-wrapper-0.drv" [color = red];
+  "/gnu/store/zv0yx4506q6k13vkl8k7pqnfirnlqplg-expat-2.1.0.drv" -> 
"/gnu/store/gj0jr0lz5siyn0ifq1vnksvfdcmd1gw7-binutils-2.25.drv" [color = red];
+  "/gnu/store/zv0yx4506q6k13vkl8k7pqnfirnlqplg-expat-2.1.0.drv" -> 
"/gnu/store/a74b2jvm3vv405a9wkd0c2v47s17alyi-gcc-4.9.3.drv" [color = red];
+  "/gnu/store/zv0yx4506q6k13vkl8k7pqnfirnlqplg-expat-2.1.0.drv" -> 
"/gnu/store/0b68fcwgd01v2mji5pyda5ag9amg7d10-glibc-2.21.drv" [color = red];
+  "/gnu/store/zv0yx4506q6k13vkl8k7pqnfirnlqplg-expat-2.1.0.drv" -> 
"/gnu/store/phkvp3cp8yqfqiv7i35j64lv671iv00x-glibc-utf8-locales-2.21.drv" 
[color = red];
+  "/gnu/store/fi3hvhhi6jfm7qmlxayrd551d3wxp115-sed-4.2.2.drv" [label = 
"sed-4.2.2", shape = box, fontname = Helvetica];
+  "/gnu/store/fi3hvhhi6jfm7qmlxayrd551d3wxp115-sed-4.2.2.drv" -> 
"/gnu/store/7176h825gaw745sdkwkgh7jip9w26w8m-tar-1.28.drv" [color = red];
+  "/gnu/store/fi3hvhhi6jfm7qmlxayrd551d3wxp115-sed-4.2.2.drv" -> 
"/gnu/store/86bbbaav5ilnr63cpihi9h03ila7cr1k-gzip-1.6.drv" [color = red];
+  "/gnu/store/fi3hvhhi6jfm7qmlxayrd551d3wxp115-sed-4.2.2.drv" -> 
"/gnu/store/ab82abh8l2dr56j0s9z68v6i872dic45-bzip2-1.0.6.drv" [color = red];
+  "/gnu/store/fi3hvhhi6jfm7qmlxayrd551d3wxp115-sed-4.2.2.drv" -> 
"/gnu/store/x9dji1ms9cj9vbl15bksci7w76zcwvkj-xz-5.0.4.drv" [color = red];
+  "/gnu/store/fi3hvhhi6jfm7qmlxayrd551d3wxp115-sed-4.2.2.drv" -> 
"/gnu/store/l8712bh52742kzav8w11n97l0vzim838-file-5.22.drv" [color = red];
+  "/gnu/store/fi3hvhhi6jfm7qmlxayrd551d3wxp115-sed-4.2.2.drv" -> 
"/gnu/store/52b3d4q4nyv0i0n1phnwms3xan38q8is-diffutils-3.3.drv" [color = red];
+  "/gnu/store/fi3hvhhi6jfm7qmlxayrd551d3wxp115-sed-4.2.2.drv" -> 
"/gnu/store/vaksjdasfzhbj6jzc46fdglwy75y8ggs-patch-2.7.5.drv" [color = red];
+  "/gnu/store/fi3hvhhi6jfm7qmlxayrd551d3wxp115-sed-4.2.2.drv" -> 
"/gnu/store/4dikg9vw3fnhvjx86wq5jlpaw58nfmbv-sed-4.2.2.drv" [color = red];
+  "/gnu/store/fi3hvhhi6jfm7qmlxayrd551d3wxp115-sed-4.2.2.drv" -> 
"/gnu/store/6q81294lvwzwqwj3s5flhl2fap5ac1qf-findutils-4.4.2.drv" [color = red];
+  "/gnu/store/fi3hvhhi6jfm7qmlxayrd551d3wxp115-sed-4.2.2.drv" -> 
"/gnu/store/0qzl2vj3fwz03m6a8v7yypqhb58b4408-gawk-4.1.3.drv" [color = red];
+  "/gnu/store/fi3hvhhi6jfm7qmlxayrd551d3wxp115-sed-4.2.2.drv" -> 
"/gnu/store/0w9md4s7vasgi5jy1xvxnsg4a3s134a9-grep-2.21.drv" [color = red];
+  "/gnu/store/fi3hvhhi6jfm7qmlxayrd551d3wxp115-sed-4.2.2.drv" -> 
"/gnu/store/ldxarpgn1f4xbghbw0vqibdbi5cv3snl-coreutils-8.24.drv" [color = red];
+  "/gnu/store/fi3hvhhi6jfm7qmlxayrd551d3wxp115-sed-4.2.2.drv" -> 
"/gnu/store/j7h01h99vrcs8wr0xx997wl2ijqhrz9b-make-4.1.drv" [color = red];
+  "/gnu/store/fi3hvhhi6jfm7qmlxayrd551d3wxp115-sed-4.2.2.drv" -> 
"/gnu/store/8ghhz8yi3m9d4s07r443106g4pffd8q2-bash-4.3.39.drv" [color = red];
+  "/gnu/store/fi3hvhhi6jfm7qmlxayrd551d3wxp115-sed-4.2.2.drv" -> 
"/gnu/store/sx1s4j6p0sm66znaaizb9a4cfwy109zy-ld-wrapper-0.drv" [color = red];
+  "/gnu/store/fi3hvhhi6jfm7qmlxayrd551d3wxp115-sed-4.2.2.drv" -> 
"/gnu/store/gj0jr0lz5siyn0ifq1vnksvfdcmd1gw7-binutils-2.25.drv" [color = red];
+  "/gnu/store/fi3hvhhi6jfm7qmlxayrd551d3wxp115-sed-4.2.2.drv" -> 
"/gnu/store/a74b2jvm3vv405a9wkd0c2v47s17alyi-gcc-4.9.3.drv" [color = red];
+  "/gnu/store/fi3hvhhi6jfm7qmlxayrd551d3wxp115-sed-4.2.2.drv" -> 
"/gnu/store/0b68fcwgd01v2mji5pyda5ag9amg7d10-glibc-2.21.drv" [color = red];
+  "/gnu/store/fi3hvhhi6jfm7qmlxayrd551d3wxp115-sed-4.2.2.drv" -> 
"/gnu/store/phkvp3cp8yqfqiv7i35j64lv671iv00x-glibc-utf8-locales-2.21.drv" 
[color = red];
+  "/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" [label = 
"attr-2.4.46", shape = box, fontname = Helvetica];
+  "/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" -> 
"/gnu/store/mw5q1c5flk83xwj7yvqv0mb3hi6xb316-gettext-0.19.5.drv" [color = red];
+  "/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" -> 
"/gnu/store/7176h825gaw745sdkwkgh7jip9w26w8m-tar-1.28.drv" [color = red];
+  "/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" -> 
"/gnu/store/86bbbaav5ilnr63cpihi9h03ila7cr1k-gzip-1.6.drv" [color = red];
+  "/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" -> 
"/gnu/store/ab82abh8l2dr56j0s9z68v6i872dic45-bzip2-1.0.6.drv" [color = red];
+  "/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" -> 
"/gnu/store/x9dji1ms9cj9vbl15bksci7w76zcwvkj-xz-5.0.4.drv" [color = red];
+  "/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" -> 
"/gnu/store/l8712bh52742kzav8w11n97l0vzim838-file-5.22.drv" [color = red];
+  "/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" -> 
"/gnu/store/52b3d4q4nyv0i0n1phnwms3xan38q8is-diffutils-3.3.drv" [color = red];
+  "/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" -> 
"/gnu/store/vaksjdasfzhbj6jzc46fdglwy75y8ggs-patch-2.7.5.drv" [color = red];
+  "/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" -> 
"/gnu/store/4dikg9vw3fnhvjx86wq5jlpaw58nfmbv-sed-4.2.2.drv" [color = red];
+  "/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" -> 
"/gnu/store/6q81294lvwzwqwj3s5flhl2fap5ac1qf-findutils-4.4.2.drv" [color = red];
+  "/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" -> 
"/gnu/store/0qzl2vj3fwz03m6a8v7yypqhb58b4408-gawk-4.1.3.drv" [color = red];
+  "/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" -> 
"/gnu/store/0w9md4s7vasgi5jy1xvxnsg4a3s134a9-grep-2.21.drv" [color = red];
+  "/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" -> 
"/gnu/store/ldxarpgn1f4xbghbw0vqibdbi5cv3snl-coreutils-8.24.drv" [color = red];
+  "/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" -> 
"/gnu/store/j7h01h99vrcs8wr0xx997wl2ijqhrz9b-make-4.1.drv" [color = red];
+  "/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" -> 
"/gnu/store/8ghhz8yi3m9d4s07r443106g4pffd8q2-bash-4.3.39.drv" [color = red];
+  "/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" -> 
"/gnu/store/sx1s4j6p0sm66znaaizb9a4cfwy109zy-ld-wrapper-0.drv" [color = red];
+  "/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" -> 
"/gnu/store/gj0jr0lz5siyn0ifq1vnksvfdcmd1gw7-binutils-2.25.drv" [color = red];
+  "/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" -> 
"/gnu/store/a74b2jvm3vv405a9wkd0c2v47s17alyi-gcc-4.9.3.drv" [color = red];
+  "/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" -> 
"/gnu/store/0b68fcwgd01v2mji5pyda5ag9amg7d10-glibc-2.21.drv" [color = red];
+  "/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" -> 
"/gnu/store/phkvp3cp8yqfqiv7i35j64lv671iv00x-glibc-utf8-locales-2.21.drv" 
[color = red];
+  "/gnu/store/ww221j42552c05xrrbjjz9av7w7r9s5x-attr-2.4.46.drv" -> 
"/gnu/store/1vx9x5ml5q6irf4pnjrlfgmhcislmg1n-perl-5.16.1.drv" [color = red];
+  "/gnu/store/1jzdjkign6jjsanmfhzs45kngi9gw5k8-gmp-6.0.0a.drv" [label = 
"gmp-6.0.0a", shape = box, fontname = Helvetica];
+  "/gnu/store/1jzdjkign6jjsanmfhzs45kngi9gw5k8-gmp-6.0.0a.drv" -> 
"/gnu/store/zsi7a1v45zq9qc7fzzw463q907mwqwwr-m4-1.4.17.drv" [color = red];
+  "/gnu/store/1jzdjkign6jjsanmfhzs45kngi9gw5k8-gmp-6.0.0a.drv" -> 
"/gnu/store/7176h825gaw745sdkwkgh7jip9w26w8m-tar-1.28.drv" [color = red];
+  "/gnu/store/1jzdjkign6jjsanmfhzs45kngi9gw5k8-gmp-6.0.0a.drv" -> 
"/gnu/store/86bbbaav5ilnr63cpihi9h03ila7cr1k-gzip-1.6.drv" [color = red];
+  "/gnu/store/1jzdjkign6jjsanmfhzs45kngi9gw5k8-gmp-6.0.0a.drv" -> 
"/gnu/store/ab82abh8l2dr56j0s9z68v6i872dic45-bzip2-1.0.6.drv" [color = red];
+  "/gnu/store/1jzdjkign6jjsanmfhzs45kngi9gw5k8-gmp-6.0.0a.drv" -> 
"/gnu/store/x9dji1ms9cj9vbl15bksci7w76zcwvkj-xz-5.0.4.drv" [color = red];
+  "/gnu/store/1jzdjkign6jjsanmfhzs45kngi9gw5k8-gmp-6.0.0a.drv" -> 
"/gnu/store/l8712bh52742kzav8w11n97l0vzim838-file-5.22.drv" [color = red];
+  "/gnu/store/1jzdjkign6jjsanmfhzs45kngi9gw5k8-gmp-6.0.0a.drv" -> 
"/gnu/store/52b3d4q4nyv0i0n1phnwms3xan38q8is-diffutils-3.3.drv" [color = red];
+  "/gnu/store/1jzdjkign6jjsanmfhzs45kngi9gw5k8-gmp-6.0.0a.drv" -> 
"/gnu/store/vaksjdasfzhbj6jzc46fdglwy75y8ggs-patch-2.7.5.drv" [color = red];
+  "/gnu/store/1jzdjkign6jjsanmfhzs45kngi9gw5k8-gmp-6.0.0a.drv" -> 
"/gnu/store/4dikg9vw3fnhvjx86wq5jlpaw58nfmbv-sed-4.2.2.drv" [color = red];
+  "/gnu/store/1jzdjkign6jjsanmfhzs45kngi9gw5k8-gmp-6.0.0a.drv" -> 
"/gnu/store/6q81294lvwzwqwj3s5flhl2fap5ac1qf-findutils-4.4.2.drv" [color = red];
+  "/gnu/store/1jzdjkign6jjsanmfhzs45kngi9gw5k8-gmp-6.0.0a.drv" -> 
"/gnu/store/0qzl2vj3fwz03m6a8v7yypqhb58b4408-gawk-4.1.3.drv" [color = red];
+  "/gnu/store/1jzdjkign6jjsanmfhzs45kngi9gw5k8-gmp-6.0.0a.drv" -> 
"/gnu/store/0w9md4s7vasgi5jy1xvxnsg4a3s134a9-grep-2.21.drv" [color = red];
+  "/gnu/store/1jzdjkign6jjsanmfhzs45kngi9gw5k8-gmp-6.0.0a.drv" -> 
"/gnu/store/ldxarpgn1f4xbghbw0vqibdbi5cv3snl-coreutils-8.24.drv" [color = red];
+  "/gnu/store/1jzdjkign6jjsanmfhzs45kngi9gw5k8-gmp-6.0.0a.drv" -> 
"/gnu/store/j7h01h99vrcs8wr0xx997wl2ijqhrz9b-make-4.1.drv" [color = red];
+  "/gnu/store/1jzdjkign6jjsanmfhzs45kngi9gw5k8-gmp-6.0.0a.drv" -> 
"/gnu/store/8ghhz8yi3m9d4s07r443106g4pffd8q2-bash-4.3.39.drv" [color = red];
+  "/gnu/store/1jzdjkign6jjsanmfhzs45kngi9gw5k8-gmp-6.0.0a.drv" -> 
"/gnu/store/sx1s4j6p0sm66znaaizb9a4cfwy109zy-ld-wrapper-0.drv" [color = red];
+  "/gnu/store/1jzdjkign6jjsanmfhzs45kngi9gw5k8-gmp-6.0.0a.drv" -> 
"/gnu/store/gj0jr0lz5siyn0ifq1vnksvfdcmd1gw7-binutils-2.25.drv" [color = red];
+  "/gnu/store/1jzdjkign6jjsanmfhzs45kngi9gw5k8-gmp-6.0.0a.drv" -> 
"/gnu/store/a74b2jvm3vv405a9wkd0c2v47s17alyi-gcc-4.9.3.drv" [color = red];
+  "/gnu/store/1jzdjkign6jjsanmfhzs45kngi9gw5k8-gmp-6.0.0a.drv" -> 
"/gnu/store/0b68fcwgd01v2mji5pyda5ag9amg7d10-glibc-2.21.drv" [color = red];
+  "/gnu/store/1jzdjkign6jjsanmfhzs45kngi9gw5k8-gmp-6.0.0a.drv" -> 
"/gnu/store/phkvp3cp8yqfqiv7i35j64lv671iv00x-glibc-utf8-locales-2.21.drv" 
[color = red];
+  "/gnu/store/zsi7a1v45zq9qc7fzzw463q907mwqwwr-m4-1.4.17.drv" [label = 
"m4-1.4.17", shape = box, fontname = Helvetica];
+  "/gnu/store/zsi7a1v45zq9qc7fzzw463q907mwqwwr-m4-1.4.17.drv" -> 
"/gnu/store/7176h825gaw745sdkwkgh7jip9w26w8m-tar-1.28.drv" [color = red];
+  "/gnu/store/zsi7a1v45zq9qc7fzzw463q907mwqwwr-m4-1.4.17.drv" -> 
"/gnu/store/86bbbaav5ilnr63cpihi9h03ila7cr1k-gzip-1.6.drv" [color = red];
+  "/gnu/store/zsi7a1v45zq9qc7fzzw463q907mwqwwr-m4-1.4.17.drv" -> 
"/gnu/store/ab82abh8l2dr56j0s9z68v6i872dic45-bzip2-1.0.6.drv" [color = red];
+  "/gnu/store/zsi7a1v45zq9qc7fzzw463q907mwqwwr-m4-1.4.17.drv" -> 
"/gnu/store/x9dji1ms9cj9vbl15bksci7w76zcwvkj-xz-5.0.4.drv" [color = red];
+  "/gnu/store/zsi7a1v45zq9qc7fzzw463q907mwqwwr-m4-1.4.17.drv" -> 
"/gnu/store/l8712bh52742kzav8w11n97l0vzim838-file-5.22.drv" [color = red];
+  "/gnu/store/zsi7a1v45zq9qc7fzzw463q907mwqwwr-m4-1.4.17.drv" -> 
"/gnu/store/52b3d4q4nyv0i0n1phnwms3xan38q8is-diffutils-3.3.drv" [color = red];
+  "/gnu/store/zsi7a1v45zq9qc7fzzw463q907mwqwwr-m4-1.4.17.drv" -> 
"/gnu/store/vaksjdasfzhbj6jzc46fdglwy75y8ggs-patch-2.7.5.drv" [color = red];
+  "/gnu/store/zsi7a1v45zq9qc7fzzw463q907mwqwwr-m4-1.4.17.drv" -> 
"/gnu/store/4dikg9vw3fnhvjx86wq5jlpaw58nfmbv-sed-4.2.2.drv" [color = red];
+  "/gnu/store/zsi7a1v45zq9qc7fzzw463q907mwqwwr-m4-1.4.17.drv" -> 
"/gnu/store/6q81294lvwzwqwj3s5flhl2fap5ac1qf-findutils-4.4.2.drv" [color = red];
+  "/gnu/store/zsi7a1v45zq9qc7fzzw463q907mwqwwr-m4-1.4.17.drv" -> 
"/gnu/store/0qzl2vj3fwz03m6a8v7yypqhb58b4408-gawk-4.1.3.drv" [color = red];
+  "/gnu/store/zsi7a1v45zq9qc7fzzw463q907mwqwwr-m4-1.4.17.drv" -> 
"/gnu/store/0w9md4s7vasgi5jy1xvxnsg4a3s134a9-grep-2.21.drv" [color = red];
+  "/gnu/store/zsi7a1v45zq9qc7fzzw463q907mwqwwr-m4-1.4.17.drv" -> 
"/gnu/store/ldxarpgn1f4xbghbw0vqibdbi5cv3snl-coreutils-8.24.drv" [color = red];
+  "/gnu/store/zsi7a1v45zq9qc7fzzw463q907mwqwwr-m4-1.4.17.drv" -> 
"/gnu/store/j7h01h99vrcs8wr0xx997wl2ijqhrz9b-make-4.1.drv" [color = red];
+  "/gnu/store/zsi7a1v45zq9qc7fzzw463q907mwqwwr-m4-1.4.17.drv" -> 
"/gnu/store/8ghhz8yi3m9d4s07r443106g4pffd8q2-bash-4.3.39.drv" [color = red];
+  "/gnu/store/zsi7a1v45zq9qc7fzzw463q907mwqwwr-m4-1.4.17.drv" -> 
"/gnu/store/sx1s4j6p0sm66znaaizb9a4cfwy109zy-ld-wrapper-0.drv" [color = red];
+  "/gnu/store/zsi7a1v45zq9qc7fzzw463q907mwqwwr-m4-1.4.17.drv" -> 
"/gnu/store/gj0jr0lz5siyn0ifq1vnksvfdcmd1gw7-binutils-2.25.drv" [color = red];
+  "/gnu/store/zsi7a1v45zq9qc7fzzw463q907mwqwwr-m4-1.4.17.drv" -> 
"/gnu/store/a74b2jvm3vv405a9wkd0c2v47s17alyi-gcc-4.9.3.drv" [color = red];
+  "/gnu/store/zsi7a1v45zq9qc7fzzw463q907mwqwwr-m4-1.4.17.drv" -> 
"/gnu/store/0b68fcwgd01v2mji5pyda5ag9amg7d10-glibc-2.21.drv" [color = red];
+  "/gnu/store/zsi7a1v45zq9qc7fzzw463q907mwqwwr-m4-1.4.17.drv" -> 
"/gnu/store/phkvp3cp8yqfqiv7i35j64lv671iv00x-glibc-utf8-locales-2.21.drv" 
[color = red];
+
+}
diff --git a/doc/images/coreutils-graph.dot b/doc/images/coreutils-graph.dot
new file mode 100644
index 0000000..f9a08a8
--- /dev/null
+++ b/doc/images/coreutils-graph.dot
@@ -0,0 +1,23 @@
+digraph "Guix package" {
+  "50639808" [label = "coreutils-8.24", shape = box, fontname = Helvetica];
+  "50639808" -> "31984640" [color = red];
+  "50639808" -> "50641344" [color = red];
+  "50639808" -> "38428672" [color = red];
+  "31984640" [label = "perl-5.16.1", shape = box, fontname = Helvetica];
+  "50641344" [label = "acl-2.2.52", shape = box, fontname = Helvetica];
+  "50641344" -> "50641728" [color = red];
+  "50641344" -> "31984640" [color = red];
+  "50641344" -> "50640768" [color = red];
+  "50641344" -> "50641536" [color = red];
+  "50641728" [label = "gettext-0.19.5", shape = box, fontname = Helvetica];
+  "50641728" -> "50530368" [color = red];
+  "50530368" [label = "expat-2.1.0", shape = box, fontname = Helvetica];
+  "50640768" [label = "sed-4.2.2", shape = box, fontname = Helvetica];
+  "50641536" [label = "attr-2.4.46", shape = box, fontname = Helvetica];
+  "50641536" -> "50641728" [color = red];
+  "50641536" -> "31984640" [color = red];
+  "38428672" [label = "gmp-6.0.0a", shape = box, fontname = Helvetica];
+  "38428672" -> "38431168" [color = red];
+  "38431168" [label = "m4-1.4.17", shape = box, fontname = Helvetica];
+
+}
diff --git a/guix/scripts/graph.scm b/guix/scripts/graph.scm
new file mode 100644
index 0000000..475f054
--- /dev/null
+++ b/guix/scripts/graph.scm
@@ -0,0 +1,426 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2015 Ludovic Courtès <address@hidden>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix scripts graph)
+  #:use-module (guix ui)
+  #:use-module (guix utils)
+  #:use-module (guix packages)
+  #:use-module (guix monads)
+  #:use-module (guix store)
+  #:use-module (guix gexp)
+  #:use-module (guix derivations)
+  #:use-module ((guix build-system gnu) #:select (standard-packages))
+  #:use-module (gnu packages)
+  #:use-module (guix sets)
+  #:use-module (guix records)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-9)
+  #:use-module (srfi srfi-34)
+  #:use-module (srfi srfi-37)
+  #:use-module (ice-9 match)
+  #:use-module (web uri)
+  #:export (%package-node-type
+            %bag-node-type
+            %bag-emerged-node-type
+            %derivation-node-type
+            %reference-node-type
+
+            %graphviz-backend
+            graph-backend?
+            graph-backend
+
+            export-graph
+
+            guix-graph))
+
+
+;;;
+;;; Node types.
+;;;
+
+(define-record-type* <node-type> node-type make-node-type
+  node-type?
+  (identifier  node-type-identifier)              ;node -> M identifier
+  (label       node-type-label)                   ;node -> string
+  (edges       node-type-edges)                   ;node -> M list of nodes
+  (convert     node-type-convert                  ;package -> M list of nodes
+               (default (lift1 list %store-monad)))
+  (name        node-type-name)                    ;string
+  (description node-type-description))            ;string
+
+
+;;;
+;;; Package DAG.
+;;;
+
+(define (uri->file-name uri)
+  "Return the 'base name' of URI or URI itself, where URI is a string."
+  (let ((path (and=> (string->uri uri) uri-path)))
+    (if path
+        (basename path)
+        uri)))
+
+(define (node-full-name thing)
+  "Return a human-readable name to denote THING, a package, origin, or file
+name."
+  (cond ((package? thing)
+         (package-full-name thing))
+        ((origin? thing)
+         (or (origin-file-name thing)
+             (match (origin-uri thing)
+               ((head . tail)
+                (uri->file-name head))
+               ((? string? uri)
+                (uri->file-name uri)))))
+        ((string? thing)                          ;file name
+         (or (basename thing)
+             (error "basename" thing)))
+        (else
+         (number->string (object-address thing) 16))))
+
+(define (package-node-edges package)
+  "Return the list of dependencies of PACKAGE."
+  (match (package-direct-inputs package)
+    (((labels packages . outputs) ...)
+     ;; Filter out origins and other non-package dependencies.
+     (filter package? packages))))
+
+(define %package-node-type
+  ;; Type for the traversal of package nodes.
+  (node-type
+   (name "package")
+   (description "the DAG of packages, excluding implicit inputs")
+
+   ;; We use package addresses as unique identifiers.  This generally works
+   ;; well, but for generated package objects, we could end up with two
+   ;; packages that are not 'eq?', yet map to the same derivation (XXX).
+   (identifier (lift1 object-address %store-monad))
+   (label node-full-name)
+   (edges (lift1 package-node-edges %store-monad))))
+
+
+;;;
+;;; Package DAG using bags.
+;;;
+
+(define (bag-node-identifier thing)
+  "Return a unique identifier for THING, which may be a package, origin, or a
+file name."
+  ;; If THING is a file name (a string), we just return it; if it's a package
+  ;; or origin, we return its address.  That gives us the object graph, but
+  ;; that may differ from the derivation graph (for instance,
+  ;; 'package-with-bootstrap-guile' generates fresh package objects, and
+  ;; several packages that are not 'eq?' may actually map to the same
+  ;; derivation.)  Thus, we lower THING and use its derivation file name as a
+  ;; unique identifier.
+  (with-monad %store-monad
+    (if (string? thing)
+        (return thing)
+        (mlet %store-monad ((low (lower-object thing)))
+          (return (if (derivation? low)
+                      (derivation-file-name low)
+                      low))))))
+
+(define (bag-node-edges thing)
+  "Return the list of dependencies of THING, a package or origin, etc."
+  (if (package? thing)
+      (match (bag-direct-inputs (package->bag thing))
+        (((labels things . outputs) ...)
+         (filter-map (match-lambda
+                       ((? package? p) p)
+                       ;; XXX: Here we choose to filter out origins, files,
+                       ;; etc.  Replace "#f" with "x" to reinstate them.
+                       (x #f))
+                     things)))
+      '()))
+
+(define %bag-node-type
+  ;; Type for the traversal of package nodes via the "bag" representation,
+  ;; which includes implicit inputs.
+  (node-type
+   (name "bag")
+   (description "the DAG of packages, including implicit inputs")
+   (identifier bag-node-identifier)
+   (label node-full-name)
+   (edges (lift1 bag-node-edges %store-monad))))
+
+(define standard-package-set
+  (memoize
+   (lambda ()
+     "Return the set of standard packages provided by GNU-BUILD-SYSTEM."
+     (match (standard-packages)
+       (((labels packages . output) ...)
+        (list->setq packages))))))
+
+(define (bag-node-edges-sans-bootstrap thing)
+  "Like 'bag-node-edges', but pretend that the standard packages of
+GNU-BUILD-SYSTEM have zero dependencies."
+  (if (set-contains? (standard-package-set) thing)
+      '()
+      (bag-node-edges thing)))
+
+(define %bag-emerged-node-type
+  ;; Like %BAG-NODE-TYPE, but without the bootstrap subset of the DAG.
+  (node-type
+   (name "bag-emerged")
+   (description "same as 'bag', but without the bootstrap nodes")
+   (identifier bag-node-identifier)
+   (label node-full-name)
+   (edges (lift1 bag-node-edges-sans-bootstrap %store-monad))))
+
+
+;;;
+;;; Derivation DAG.
+;;;
+
+(define (file->derivation file)
+  "Read the derivation from FILE and return it."
+  (call-with-input-file file read-derivation))
+
+(define (derivation-dependencies obj)
+  "Return the <derivation> objects and store items corresponding to the
+dependencies of OBJ, a <derivation> or store item."
+  (if (derivation? obj)
+      (append (map (compose file->derivation derivation-input-path)
+                   (derivation-inputs obj))
+              (derivation-sources obj))
+      '()))
+
+(define (derivation-node-identifier node)
+  "Return a unique identifier for NODE, which may be either a <derivation> or
+a plain store file."
+  (if (derivation? node)
+      (derivation-file-name node)
+      node))
+
+(define (derivation-node-label node)
+  "Return a label for NODE, a <derivation> object or plain store item."
+  (store-path-package-name (match node
+                             ((? derivation? drv)
+                              (derivation-file-name drv))
+                             ((? string? file)
+                              file))))
+
+(define %derivation-node-type
+  ;; DAG of derivations.  Very accurate, very detailed, but usually too much
+  ;; detailed.
+  (node-type
+   (name "derivation")
+   (description "the DAG of derivations")
+   (convert (lambda (package)
+              (with-monad %store-monad
+                (>>= (package->derivation package)
+                     (lift1 list %store-monad)))))
+   (identifier (lift1 derivation-node-identifier %store-monad))
+   (label derivation-node-label)
+   (edges (lift1 derivation-dependencies %store-monad))))
+
+
+;;;
+;;; DAG of residual references (aka. run-time dependencies).
+;;;
+
+(define (references* item)
+  "Return as a monadic value the references of ITEM, based either on the
+information available in the local store or using information about
+substitutes."
+  (lambda (store)
+    (guard (c ((nix-protocol-error? c)
+               (match (substitutable-path-info store (list item))
+                 ((info)
+                  (values (substitutable-references info) store))
+                 (()
+                  (leave (_ "references for '~a' are not known~%")
+                         item)))))
+      (values (references store item) store))))
+
+(define %reference-node-type
+  (node-type
+   (name "references")
+   (description "the DAG of run-time dependencies (store references)")
+   (convert (lambda (package)
+              ;; Return the output file names of PACKAGE.
+              (mlet %store-monad ((drv (package->derivation package)))
+                (return (match (derivation->output-paths drv)
+                          (((_ . file-names) ...)
+                           file-names))))))
+   (identifier (lift1 identity %store-monad))
+   (label store-path-package-name)
+   (edges references*)))
+
+
+;;;
+;;; List of node types.
+;;;
+
+(define %node-types
+  ;; List of all the node types.
+  (list %package-node-type
+        %bag-node-type
+        %bag-emerged-node-type
+        %derivation-node-type
+        %reference-node-type))
+
+(define (lookup-node-type name)
+  "Return the node type called NAME.  Raise an error if it is not found."
+  (or (find (lambda (type)
+              (string=? (node-type-name type) name))
+            %node-types)
+      (leave (_ "~a: unknown node type~%") name)))
+
+(define (list-node-types)
+  "Print the available node types along with their synopsis."
+  (display (_ "The available node types are:\n"))
+  (newline)
+  (for-each (lambda (type)
+              (format #t "  - ~a: ~a~%"
+                      (node-type-name type)
+                      (node-type-description type)))
+            %node-types))
+
+
+;;;
+;;; Graphviz export.
+;;;
+
+(define-record-type <graph-backend>
+  (graph-backend prologue epilogue node edge)
+  graph-backend?
+  (prologue graph-backend-prologue)
+  (epilogue graph-backend-epilogue)
+  (node     graph-backend-node)
+  (edge     graph-backend-edge))
+
+(define (emit-prologue name port)
+  (format port "digraph \"Guix ~a\" {\n"
+          name))
+(define (emit-epilogue port)
+  (display "\n}\n" port))
+(define (emit-node id label port)
+  (format port "  \"~a\" [label = \"~a\", shape = box, fontname = 
Helvetica];~%"
+          id label))
+(define (emit-edge id1 id2 port)
+  (format port "  \"~a\" -> \"~a\" [color = red];~%"
+          id1 id2))
+
+(define %graphviz-backend
+  (graph-backend emit-prologue emit-epilogue
+                 emit-node emit-edge))
+
+(define* (export-graph sinks port
+                       #:key
+                       (node-type %package-node-type)
+                       (backend %graphviz-backend))
+  "Write to PORT the representation of the DAG with the given SINKS, using the
+given BACKEND.  Use NODE-TYPE to traverse the DAG."
+  (match backend
+    (($ <graph-backend> emit-prologue emit-epilogue emit-node emit-edge)
+     (emit-prologue (node-type-name node-type) port)
+
+     (match node-type
+       (($ <node-type> node-identifier node-label node-edges)
+        (let loop ((nodes   sinks)
+                   (visited (set)))
+          (match nodes
+            (()
+             (with-monad %store-monad
+               (emit-epilogue port)
+               (store-return #t)))
+            ((head . tail)
+             (mlet %store-monad ((id (node-identifier head)))
+               (if (set-contains? visited id)
+                   (loop tail visited)
+                   (mlet* %store-monad ((dependencies (node-edges head))
+                                        (ids          (mapm %store-monad
+                                                            node-identifier
+                                                            dependencies)))
+                     (emit-node id (node-label head) port)
+                     (for-each (lambda (dependency dependency-id)
+                                 (emit-edge id dependency-id port))
+                               dependencies ids)
+                     (loop (append dependencies tail)
+                           (set-insert id visited)))))))))))))
+
+
+;;;
+;;; Command-line options.
+;;;
+
+(define %options
+  (list (option '(#\t "type") #t #f
+                (lambda (opt name arg result)
+                  (alist-cons 'node-type (lookup-node-type arg)
+                              result)))
+        (option '("list-types") #f #f
+                (lambda (opt name arg result)
+                  (list-node-types)
+                  (exit 0)))
+        (option '(#\h "help") #f #f
+                (lambda args
+                  (show-help)
+                  (exit 0)))
+        (option '(#\V "version") #f #f
+                (lambda args
+                  (show-version-and-exit "guix edit")))))
+
+(define (show-help)
+  ;; TRANSLATORS: Here 'dot' is the name of a program; it must not be
+  ;; translated.
+  (display (_ "Usage: guix graph PACKAGE...
+Emit a Graphviz (dot) representation of the dependencies of PACKAGE...\n"))
+  (display (_ "
+  -t, --type=TYPE        represent nodes of the given TYPE"))
+  (display (_ "
+      --list-types       list the available graph types"))
+  (newline)
+  (display (_ "
+  -h, --help             display this help and exit"))
+  (display (_ "
+  -V, --version          display version information and exit"))
+  (newline)
+  (show-bug-report-information))
+
+(define %default-options
+  `((node-type . ,%package-node-type)))
+
+
+;;;
+;;; Entry point.
+;;;
+
+(define (guix-graph . args)
+  (with-error-handling
+    (let* ((opts     (parse-command-line args %options
+                                         (list %default-options)))
+           (specs    (filter-map (match-lambda
+                                   (('argument . spec) spec)
+                                   (_ #f))
+                                 opts))
+           (type     (assoc-ref opts 'node-type))
+           (packages (map specification->package specs)))
+      (with-store store
+        (run-with-store store
+          (mlet %store-monad ((nodes (mapm %store-monad
+                                           (node-type-convert type)
+                                           packages)))
+            (export-graph (concatenate nodes)
+                          (current-output-port)
+                          #:node-type type))))))
+  #t)
+
+;;; graph.scm ends here
diff --git a/tests/graph.scm b/tests/graph.scm
new file mode 100644
index 0000000..f454b06
--- /dev/null
+++ b/tests/graph.scm
@@ -0,0 +1,193 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2015 Ludovic Courtès <address@hidden>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (test-graph)
+  #:use-module (guix tests)
+  #:use-module (guix scripts graph)
+  #:use-module (guix packages)
+  #:use-module (guix derivations)
+  #:use-module (guix store)
+  #:use-module (guix monads)
+  #:use-module (guix build-system gnu)
+  #:use-module (guix gexp)
+  #:use-module (gnu packages)
+  #:use-module (gnu packages bootstrap)
+  #:use-module (ice-9 match)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-11)
+  #:use-module (srfi srfi-26)
+  #:use-module (srfi srfi-64))
+
+(define %store
+  (open-connection-for-tests))
+
+(define (make-recording-backend)
+  "Return a <graph-backend> and a thunk that returns the recorded nodes and
+edges."
+  (let ((nodes '())
+        (edges '()))
+    (define (record-node id label port)
+      (set! nodes (cons (list id label) nodes)))
+    (define (record-edge source target port)
+      (set! edges (cons (list source target) edges)))
+    (define (return)
+      (values (reverse nodes) (reverse edges)))
+
+    (values (graph-backend (const #t) (const #t)
+                           record-node record-edge)
+            return)))
+
+(define (package->tuple package)
+  "Return a tuple representing PACKAGE as produced by %PACKAGE-NODE-TYPE."
+  (list (object-address package)
+        (package-full-name package)))
+
+(define (edge->tuple source target)
+  "Likewise for an edge from SOURCE to TARGET."
+  (list (object-address source)
+        (object-address target)))
+
+
+(test-begin "graph")
+
+(test-assert "package DAG"
+  (let-values (((backend nodes+edges) (make-recording-backend)))
+    (let* ((p1 (dummy-package "p1"))
+           (p2 (dummy-package "p2" (inputs `(("p1" ,p1)))))
+           (p3 (dummy-package "p3" (inputs `(("p2" ,p2) ("p1", p1))))))
+      (run-with-store %store
+        (export-graph (list p3) 'port
+                      #:node-type %package-node-type
+                      #:backend backend))
+      ;; We should see nothing more than these 3 packages.
+      (let-values (((nodes edges) (nodes+edges)))
+        (and (equal? nodes (map package->tuple (list p3 p2 p1)))
+             (equal? edges
+                     (map edge->tuple
+                          (list p3 p3 p2)
+                          (list p2 p1 p1))))))))
+
+(test-assert "bag-emerged DAG"
+  (let-values (((backend nodes+edges) (make-recording-backend)))
+    (let ((p        (dummy-package "p"))
+          (implicit (map (match-lambda
+                           ((label package) package))
+                         (standard-packages))))
+      (run-with-store %store
+        (export-graph (list p) 'port
+                      #:node-type %bag-emerged-node-type
+                      #:backend backend))
+      ;; We should see exactly P and IMPLICIT, with one edge from P to each
+      ;; element of IMPLICIT.
+      (let-values (((nodes edges) (nodes+edges)))
+        (and (equal? (match nodes
+                       (((labels names) ...)
+                        names))
+                     (map package-full-name (cons p implicit)))
+             (equal? (match edges
+                       (((sources destinations) ...)
+                        (zip (map store-path-package-name sources)
+                             (map store-path-package-name destinations))))
+                     (map (lambda (destination)
+                            (list "p-0.drv"
+                                  (string-append
+                                   (package-full-name destination)
+                                   ".drv")))
+                          implicit)))))))
+
+(test-assert "bag DAG"
+  (let-values (((backend nodes+edges) (make-recording-backend)))
+    (let ((p (dummy-package "p")))
+      (run-with-store %store
+        (export-graph (list p) 'port
+                      #:node-type %bag-node-type
+                      #:backend backend))
+      ;; We should see P, its implicit inputs as well as the whole DAG, which
+      ;; should include bootstrap binaries.
+      (let-values (((nodes edges) (nodes+edges)))
+        (every (lambda (name)
+                 (find (cut string=? name <>)
+                       (match nodes
+                         (((labels names) ...)
+                          names))))
+               (match %bootstrap-inputs
+                 (((labels packages) ...)
+                  (map package-full-name packages))))))))
+
+(test-assert "derivation DAG"
+  (let-values (((backend nodes+edges) (make-recording-backend)))
+    (run-with-store %store
+      (mlet* %store-monad ((txt   (text-file "text-file" "Hello!"))
+                           (guile (package->derivation %bootstrap-guile))
+                           (drv   (gexp->derivation "output"
+                                                    #~(symlink #$txt #$output)
+                                                    #:guile-for-build
+                                                    guile)))
+        ;; We should get at least these 3 nodes and corresponding edges.
+        (mbegin %store-monad
+          (export-graph (list drv) 'port
+                        #:node-type %derivation-node-type
+                        #:backend backend)
+          (let-values (((nodes edges) (nodes+edges)))
+            ;; XXX: For some reason we need to throw in some 'basename'.
+            (return (and (match nodes
+                           (((ids labels) ...)
+                            (let ((ids (map basename ids)))
+                              (every (lambda (item)
+                                       (member (basename item) ids))
+                                     (list txt
+                                           (derivation-file-name drv)
+                                           (derivation-file-name guile))))))
+                         (every (cut member <>
+                                     (map (lambda (edge)
+                                            (map basename edge))
+                                          edges))
+                                (list (map (compose basename 
derivation-file-name)
+                                           (list drv guile))
+                                      (list (basename (derivation-file-name 
drv))
+                                            (basename txt))))))))))))
+
+(test-assert "reference DAG"
+  (let-values (((backend nodes+edges) (make-recording-backend)))
+    (run-with-store %store
+      (mlet* %store-monad ((txt   (text-file "text-file" "Hello!"))
+                           (guile (package->derivation %bootstrap-guile))
+                           (drv   (gexp->derivation "output"
+                                                    #~(symlink #$txt #$output)
+                                                    #:guile-for-build
+                                                    guile))
+                           (out -> (derivation->output-path drv)))
+        ;; We should see only OUT and TXT, with an edge from the former to the
+        ;; latter.
+        (mbegin %store-monad
+          (built-derivations (list drv))
+          (export-graph (list (derivation->output-path drv)) 'port
+                        #:node-type %reference-node-type
+                        #:backend backend)
+          (let-values (((nodes edges) (nodes+edges)))
+            (return
+             (and (equal? (match nodes
+                            (((ids labels) ...)
+                             ids))
+                          (list out txt))
+                  (equal? edges `((,out ,txt)))))))))))
+
+(test-end "graph")
+
+
+(exit (= (test-runner-fail-count (test-runner-current)) 0))
diff --git a/tests/guix-graph.sh b/tests/guix-graph.sh
new file mode 100644
index 0000000..199258a
--- /dev/null
+++ b/tests/guix-graph.sh
@@ -0,0 +1,34 @@
+# GNU Guix --- Functional package management for GNU
+# Copyright © 2015 Ludovic Courtès <address@hidden>
+#
+# This file is part of GNU Guix.
+#
+# GNU Guix is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or (at
+# your option) any later version.
+#
+# GNU Guix is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+#
+# Test the 'guix graph' command-line utility.
+#
+
+guix graph --version
+
+for package in guile-bootstrap coreutils python
+do
+    for graph in package bag-emerged bag
+    do
+       guix graph -t "$graph" "$package" | grep "$package"
+    done
+done
+
+guix build guile-bootstrap
+guix graph -t references guile-bootstrap | grep guile-bootstrap



reply via email to

[Prev in Thread] Current Thread [Next in Thread]