gnu: Add perl-getopt-tabular.
[jackhill/guix/guix.git] / emacs / guix-main.scm
index 273a360..ae3a492 100644 (file)
 ;; installed manifest but not in a package directory), ‘id’ parameter is
 ;; still "name-version" string.  So ‘id’ package parameter in the code
 ;; below is either an object-address number or a full-name string.
-;;
-;; Important: as object addresses live only during guile session, elisp
-;; part should take care about updating information after "Guix REPL" is
-;; restarted (TODO!)
 
 ;; To speed-up the process of getting information, the following
 ;; auxiliary variables are used:
  (ice-9 vlist)
  (ice-9 match)
  (srfi srfi-1)
+ (srfi srfi-2)
  (srfi srfi-11)
  (srfi srfi-19)
  (srfi srfi-26)
  (guix)
+ (guix git-download)
  (guix packages)
  (guix profiles)
  (guix licenses)
  (guix utils)
  (guix ui)
  (guix scripts package)
+ (guix scripts pull)
  (gnu packages))
 
 (define-syntax-rule (first-or-false lst)
    (manifest-entry-version entry)
    (manifest-entry-output  entry)))
 
+(define (manifest-entry->package-specification entry)
+  (call-with-values
+      (lambda () (manifest-entry->name+version+output entry))
+    make-package-specification))
+
+(define (manifest-entries->package-specifications entries)
+  (map manifest-entry->package-specification entries))
+
+(define (generation-package-specifications profile number)
+  "Return a list of package specifications for generation NUMBER."
+  (let ((manifest (profile-manifest
+                   (generation-file-name profile number))))
+    (manifest-entries->package-specifications
+     (manifest-entries manifest))))
+
+(define (generation-package-specifications+paths profile number)
+  "Return a list of package specifications and paths for generation NUMBER.
+Each element of the list is a list of the package specification and its path."
+  (let ((manifest (profile-manifest
+                   (generation-file-name profile number))))
+    (map (lambda (entry)
+           (list (manifest-entry->package-specification entry)
+                 (manifest-entry-item entry)))
+         (manifest-entries manifest))))
+
+(define (generation-difference profile number1 number2)
+  "Return a list of package specifications for outputs installed in generation
+NUMBER1 and not installed in generation NUMBER2."
+  (let ((specs1 (generation-package-specifications profile number1))
+        (specs2 (generation-package-specifications profile number2)))
+    (lset-difference string=? specs1 specs2)))
+
 (define (manifest-entries->hash-table entries)
   "Return a hash table of name keys and lists of matching manifest ENTRIES."
   (let ((table (make-hash-table (length entries))))
@@ -223,6 +254,18 @@ Example:
                      (license-name license)))
               (list-maybe (package-license package))))
 
+(define (package-source-names package)
+  "Return a list of source names (URLs) of the PACKAGE."
+  (let ((source (package-source package)))
+    (and (origin? source)
+         (filter-map (lambda (uri)
+                       (cond ((string? uri)
+                              uri)
+                             ((git-reference? uri)
+                              (git-reference-url uri))
+                             (else "Unknown source type")))
+                     (list-maybe (origin-uri source))))))
+
 (define (package-unique? package)
   "Return #t if PACKAGE is a single package with such name/version."
   (null? (cdr (packages-by-name (package-name package)
@@ -234,6 +277,7 @@ Example:
     (name              . ,package-name)
     (version           . ,package-version)
     (license           . ,package-license-names)
+    (source            . ,package-source-names)
     (synopsis          . ,package-synopsis)
     (description       . ,package-description)
     (home-url          . ,package-home-page)
@@ -478,7 +522,8 @@ ENTRIES is a list of installed manifest entries."
                    '()
                    (list (obsolete-package-sexp
                           name version entries))))
-             (map sexp-by-package packages))))))
+             (map sexp-by-package packages))))
+      (_ '())))
 
   ->sexps)
 
@@ -563,7 +608,8 @@ ENTRIES is a list of installed manifest entries."
                (append-map (cut sexps-by-manifest-entry <>)
                            entries))
              (append-map (cut sexps-by-package <> output)
-                         packages))))))
+                         packages))))
+      (_ '())))
 
   ->sexps)
 
@@ -626,8 +672,15 @@ See 'entry-sexps' for details."
                       (generation-file-name profile (car search-vals))
                       profile))
          (manifest (profile-manifest profile))
-         (patterns (apply (patterns-maker entry-type search-type)
-                          manifest search-vals))
+         (patterns (if (and (eq? entry-type 'output)
+                            (eq? search-type 'generation-diff))
+                       (match search-vals
+                         ((g1 g2)
+                          (map specification->output-pattern
+                               (generation-difference profile g1 g2)))
+                         (_ '()))
+                       (apply (patterns-maker entry-type search-type)
+                              manifest search-vals)))
          (->sexps ((pattern-transformer entry-type) manifest params)))
     (append-map ->sexps patterns)))
 
@@ -636,13 +689,14 @@ See 'entry-sexps' for details."
 
 (define (generation-param-alist profile)
   "Return an alist of generation parameters and procedures for PROFILE."
-  (list
-   (cons 'id          identity)
-   (cons 'number      identity)
-   (cons 'prev-number (cut previous-generation-number profile <>))
-   (cons 'path        (cut generation-file-name profile <>))
-   (cons 'time        (lambda (gen)
-                        (time-second (generation-time profile gen))))))
+  (let ((current (generation-number profile)))
+    `((id          . ,identity)
+      (number      . ,identity)
+      (prev-number . ,(cut previous-generation-number profile <>))
+      (current     . ,(cut = current <>))
+      (path        . ,(cut generation-file-name profile <>))
+      (time        . ,(lambda (gen)
+                        (time-second (generation-time profile gen)))))))
 
 (define (matching-generations profile predicate)
   "Return a list of PROFILE generations matching PREDICATE."
@@ -661,11 +715,20 @@ If NUMBER is 0 or less, return all generations."
   "Find PROFILE's generations matching SEARCH-TYPE and SEARCH-VALS."
   (case search-type
     ((id)
-     (matching-generations profile (cut memq <> (car search-vals))))
+     (matching-generations profile (cut memq <> search-vals)))
     ((last)
      (last-generations profile (car search-vals)))
     ((all)
      (last-generations profile +inf.0))
+    ((time)
+     (match search-vals
+       ((from to)
+        (matching-generations
+         profile
+         (lambda (gen)
+           (let ((time (time-second (generation-time profile gen))))
+             (< from time to)))))
+       (_ '())))
     (else (search-type-error "generation" search-type))))
 
 (define (generation-sexps profile params search-type search-vals)
@@ -693,7 +756,7 @@ SEARCH-TYPE should be one of the following symbols:
   'installed', 'obsolete', 'generation'.
 
 - If ENTRY-TYPE is 'generation':
-  'id', 'last', 'all'.
+  'id', 'last', 'all', 'time'.
 
 PARAMS is a list of parameters for receiving.  If it is an empty list,
 get information with all available parameters, which are:
@@ -788,28 +851,70 @@ OUTPUTS is a list of package outputs (may be an empty list)."
          (new-manifest (manifest-perform-transaction
                         manifest transaction)))
     (unless (and (null? install) (null? remove))
-      (let* ((store (open-connection))
-             (derivation (run-with-store
-                          store (profile-derivation new-manifest)))
-             (derivations (list derivation))
-             (new-profile (derivation->output-path derivation)))
+      (with-store store
+        (let* ((derivation (run-with-store store
+                             (profile-derivation new-manifest)))
+               (derivations (list derivation))
+               (new-profile (derivation->output-path derivation)))
+          (set-build-options store
+                             #:print-build-trace #f
+                             #:use-substitutes? use-substitutes?)
+          (show-manifest-transaction store manifest transaction
+                                     #:dry-run? dry-run?)
+          (show-what-to-build store derivations
+                              #:use-substitutes? use-substitutes?
+                              #:dry-run? dry-run?)
+          (unless dry-run?
+            (let ((name (generation-file-name
+                         profile
+                         (+ 1 (generation-number profile)))))
+              (and (build-derivations store derivations)
+                   (let* ((entries (manifest-entries new-manifest))
+                          (count   (length entries)))
+                     (switch-symlinks name new-profile)
+                     (switch-symlinks profile name)
+                     (format #t (N_ "~a package in profile~%"
+                                    "~a packages in profile~%"
+                                    count)
+                             count))))))))))
+
+(define (delete-generations* profile generations)
+  "Delete GENERATIONS from PROFILE.
+GENERATIONS is a list of generation numbers."
+  (with-store store
+    (delete-generations store profile generations)))
+
+(define (package-source-derivation->store-path derivation)
+  "Return a store path of the package source DERIVATION."
+  (match (derivation-outputs derivation)
+    ;; Source derivation is always (("out" . derivation)).
+    (((_ . output-drv))
+     (derivation-output-path output-drv))
+    (_ #f)))
+
+(define (package-source-path package-id)
+  "Return a store file path to a source of a package PACKAGE-ID."
+  (and-let* ((package (package-by-id package-id))
+             (source  (package-source package)))
+    (with-store store
+      (package-source-derivation->store-path
+       (package-source-derivation store source)))))
+
+(define* (package-source-build-derivation package-id #:key dry-run?
+                                          (use-substitutes? #t))
+  "Build source derivation of a package PACKAGE-ID."
+  (and-let* ((package (package-by-id package-id))
+             (source  (package-source package)))
+    (with-store store
+      (let* ((derivation  (package-source-derivation store source))
+             (derivations (list derivation)))
         (set-build-options store
+                           #:print-build-trace #f
                            #:use-substitutes? use-substitutes?)
-        (manifest-show-transaction store manifest transaction
-                                   #:dry-run? dry-run?)
         (show-what-to-build store derivations
                             #:use-substitutes? use-substitutes?
                             #:dry-run? dry-run?)
         (unless dry-run?
-          (let ((name (generation-file-name
-                       profile
-                       (+ 1 (generation-number profile)))))
-            (and (build-derivations store derivations)
-                 (let* ((entries (manifest-entries new-manifest))
-                        (count   (length entries)))
-                   (switch-symlinks name new-profile)
-                   (switch-symlinks profile name)
-                   (format #t (N_ "~a package in profile~%"
-                                  "~a packages in profile~%"
-                                  count)
-                           count)))))))))
+          (build-derivations store derivations))
+        (format #t "The source store path: ~a~%"
+                (package-source-derivation->store-path derivation))))))