summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--doc/guix.texi14
-rw-r--r--guix/scripts/graph.scm53
-rw-r--r--tests/graph.scm22
3 files changed, 74 insertions, 15 deletions
diff --git a/doc/guix.texi b/doc/guix.texi
index 74733f4fd1..47fc199c6c 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -5546,6 +5546,20 @@ example, the command below produces the reference graph of your profile
 @example
 guix graph -t references `readlink -f ~/.guix-profile`
 @end example
+
+@item referrers
+This is the graph of the @dfn{referrers} of a store item, as returned by
+@command{guix gc --referrers} (@pxref{Invoking guix gc}).
+
+This relies exclusively on local information from your store.  For
+instance, let us suppose that the current Inkscape is available in 10
+profiles on your machine; @command{guix graph -t referrers inkscape}
+will show a graph rooted at Inkscape and with those 10 profiles linked
+to it.
+
+It can help determine what is preventing a store item from being garbage
+collected.
+
 @end table
 
 The available options are the following:
diff --git a/guix/scripts/graph.scm b/guix/scripts/graph.scm
index 782fca5d63..2f70d64c90 100644
--- a/guix/scripts/graph.scm
+++ b/guix/scripts/graph.scm
@@ -42,6 +42,7 @@
             %bag-emerged-node-type
             %derivation-node-type
             %reference-node-type
+            %referrer-node-type
             %node-types
 
             guix-graph))
@@ -257,6 +258,24 @@ derivation graph")))))))
 ;;; DAG of residual references (aka. run-time dependencies).
 ;;;
 
+(define ensure-store-items
+  ;; Return a list of store items as a monadic value based on the given
+  ;; argument, which may be a store item or a package.
+  (match-lambda
+    ((? package? 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)))))
+    ((? store-path? item)
+     (with-monad %store-monad
+       (return (list item))))
+    (x
+     (raise
+      (condition (&message (message "unsupported argument for \
+this type of graph")))))))
+
 (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
@@ -275,24 +294,27 @@ substitutes."
   (node-type
    (name "references")
    (description "the DAG of run-time dependencies (store references)")
-   (convert (match-lambda
-              ((? package? 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)))))
-              ((? store-path? item)
-               (with-monad %store-monad
-                 (return (list item))))
-              (x
-               (raise
-                (condition (&message (message "unsupported argument for \
-reference graph")))))))
+   (convert ensure-store-items)
    (identifier (lift1 identity %store-monad))
    (label store-path-package-name)
    (edges references*)))
 
+(define non-derivation-referrers
+  (let ((referrers (store-lift referrers)))
+    (lambda (item)
+      "Return the referrers of ITEM, except '.drv' files."
+      (mlet %store-monad ((items (referrers item)))
+        (return (remove derivation-path? items))))))
+
+(define %referrer-node-type
+  (node-type
+   (name "referrers")
+   (description "the DAG of referrers in the store")
+   (convert ensure-store-items)
+   (identifier (lift1 identity %store-monad))
+   (label store-path-package-name)
+   (edges non-derivation-referrers)))
+
 
 ;;;
 ;;; List of node types.
@@ -305,7 +327,8 @@ reference graph")))))))
         %bag-with-origins-node-type
         %bag-emerged-node-type
         %derivation-node-type
-        %reference-node-type))
+        %reference-node-type
+        %referrer-node-type))
 
 (define (lookup-node-type name)
   "Return the node type called NAME.  Raise an error if it is not found."
diff --git a/tests/graph.scm b/tests/graph.scm
index 1ce06cc817..f2e441cee6 100644
--- a/tests/graph.scm
+++ b/tests/graph.scm
@@ -232,6 +232,28 @@ edges."
                           (list out txt))
                   (equal? edges `((,out ,txt)))))))))))
 
+(test-assert "referrer DAG"
+  (let-values (((backend nodes+edges) (make-recording-backend)))
+    (run-with-store %store
+      (mlet* %store-monad ((txt   (text-file "referrer-node" (random-text)))
+                           (drv   (gexp->derivation "referrer"
+                                                    #~(symlink #$txt #$output)))
+                           (out -> (derivation->output-path drv)))
+        ;; We should see only TXT and OUT, with an edge from the former to the
+        ;; latter.
+        (mbegin %store-monad
+          (built-derivations (list drv))
+          (export-graph (list txt) 'port
+                        #:node-type %referrer-node-type
+                        #:backend backend)
+          (let-values (((nodes edges) (nodes+edges)))
+            (return
+             (and (equal? (match nodes
+                            (((ids labels) ...)
+                             ids))
+                          (list txt out))
+                  (equal? edges `((,txt ,out)))))))))))
+
 (test-assert "node-edges"
   (run-with-store %store
     (let ((packages (fold-packages cons '())))