packages: 'specification->package+output' distinguishes "no output specified".
[jackhill/guix/guix.git] / gnu / packages.scm
index cf655e7..acb247e 100644 (file)
@@ -32,6 +32,7 @@
                           mkdir-p))
   #:autoload   (guix profiles) (packages->manifest)
   #:use-module (guix describe)
+  #:use-module (guix deprecation)
   #:use-module (ice-9 vlist)
   #:use-module (ice-9 match)
   #:autoload   (ice-9 binary-ports) (put-bytevector)
@@ -53,7 +54,9 @@
             %default-package-module-path
 
             fold-packages
+            fold-available-packages
 
+            find-newest-available-packages
             find-packages-by-name
             find-package-locations
             find-best-packages-by-name
@@ -154,23 +157,26 @@ flags."
   ;; Search path for package modules.  Each item must be either a directory
   ;; name or a pair whose car is a directory and whose cdr is a sub-directory
   ;; to narrow the search.
-  (let* ((not-colon   (char-set-complement (char-set #\:)))
-         (environment (string-tokenize (or (getenv "GUIX_PACKAGE_PATH") "")
-                                       not-colon))
-         (channels    (package-path-entries)))
+  (let*-values (((not-colon)
+                 (char-set-complement (char-set #\:)))
+                ((environment)
+                 (string-tokenize (or (getenv "GUIX_PACKAGE_PATH") "")
+                                  not-colon))
+                ((channels-scm channels-go)
+                 (package-path-entries)))
     ;; Automatically add channels and items from $GUIX_PACKAGE_PATH to Guile's
     ;; search path.  For historical reasons, $GUIX_PACKAGE_PATH goes to the
     ;; front; channels go to the back so that they don't override Guix' own
     ;; modules.
     (set! %load-path
-      (append environment %load-path channels))
+      (append environment %load-path channels-scm))
     (set! %load-compiled-path
-      (append environment %load-compiled-path channels))
+      (append environment %load-compiled-path channels-go))
 
     (make-parameter
      (append environment
              %default-package-module-path
-             channels))))
+             channels-scm))))
 
 (define %patch-path
   ;; Define it after '%package-module-path' so that '%load-path' contains user
@@ -182,6 +188,73 @@ flags."
               directory))
         %load-path)))
 
+;; This procedure is used by Emacs-Guix up to 0.5.1.1, so keep it for now.
+;; See <https://github.com/alezost/guix.el/issues/30>.
+(define-deprecated find-newest-available-packages
+  find-packages-by-name
+  (mlambda ()
+    "Return a vhash keyed by package names, and with
+associated values of the form
+
+  (newest-version newest-package ...)
+
+where the preferred package is listed first."
+    (fold-packages (lambda (p r)
+                     (let ((name    (package-name p))
+                           (version (package-version p)))
+                       (match (vhash-assoc name r)
+                         ((_ newest-so-far . pkgs)
+                          (case (version-compare version newest-so-far)
+                            ((>) (vhash-cons name `(,version ,p) r))
+                            ((=) (vhash-cons name `(,version ,p ,@pkgs) r))
+                            ((<) r)))
+                         (#f (vhash-cons name `(,version ,p) r)))))
+                   vlist-null)))
+
+(define (fold-available-packages proc init)
+  "Fold PROC over the list of available packages.  For each available package,
+PROC is called along these lines:
+
+  (PROC NAME VERSION RESULT
+        #:outputs OUTPUTS
+        #:location LOCATION
+        …)
+
+PROC can use #:allow-other-keys to ignore the bits it's not interested in.
+When a package cache is available, this procedure does not actually load any
+package module."
+  (define cache
+    (load-package-cache (current-profile)))
+
+  (if (and cache (cache-is-authoritative?))
+      (vhash-fold (lambda (name vector result)
+                    (match vector
+                      (#(name version module symbol outputs
+                              supported? deprecated?
+                              file line column)
+                       (proc name version result
+                             #:outputs outputs
+                             #:location (and file
+                                             (location file line column))
+                             #:supported? supported?
+                             #:deprecated? deprecated?))))
+                  init
+                  cache)
+      (fold-packages (lambda (package result)
+                       (proc (package-name package)
+                             (package-version package)
+                             result
+                             #:outputs (package-outputs package)
+                             #:location (package-location package)
+                             #:supported?
+                             (->bool
+                              (member (%current-system)
+                                      (package-supported-systems package)))
+                             #:deprecated?
+                             (->bool
+                              (package-superseded package))))
+                     init)))
+
 (define* (fold-packages proc init
                         #:optional
                         (modules (all-modules (%package-module-path)
@@ -326,34 +399,41 @@ reducing the memory footprint."
   (define cache-file
     (string-append directory %package-cache-file))
 
-  (define (expand-cache module symbol variable result)
+  (define (expand-cache module symbol variable result+seen)
     (match (false-if-exception (variable-ref variable))
       ((? package? package)
-       (if (hidden-package? package)
-           result
-           (cons `#(,(package-name package)
-                    ,(package-version package)
-                    ,(module-name module)
-                    ,symbol
-                    ,(package-outputs package)
-                    ,(->bool (member (%current-system)
-                                     (package-supported-systems package)))
-                    ,(->bool (package-superseded package))
-                    ,@(let ((loc (package-location package)))
-                        (if loc
-                            `(,(location-file loc)
-                              ,(location-line loc)
-                              ,(location-column loc))
-                            '(#f #f #f))))
-                 result)))
+       (match result+seen
+         ((result . seen)
+          (if (or (vhash-assq package seen)
+                  (hidden-package? package))
+              (cons result seen)
+              (cons (cons `#(,(package-name package)
+                             ,(package-version package)
+                             ,(module-name module)
+                             ,symbol
+                             ,(package-outputs package)
+                             ,(->bool
+                               (member (%current-system)
+                                       (package-supported-systems package)))
+                             ,(->bool (package-superseded package))
+                             ,@(let ((loc (package-location package)))
+                                 (if loc
+                                     `(,(location-file loc)
+                                       ,(location-line loc)
+                                       ,(location-column loc))
+                                     '(#f #f #f))))
+                          result)
+                    (vhash-consq package #t seen))))))
       (_
-       result)))
+       result+seen)))
 
   (define exp
-    (fold-module-public-variables* expand-cache '()
-                                   (all-modules (%package-module-path)
-                                                #:warn
-                                                warn-about-load-error)))
+    (first
+     (fold-module-public-variables* expand-cache
+                                    (cons '() vlist-null)
+                                    (all-modules (%package-module-path)
+                                                 #:warn
+                                                 warn-about-load-error))))
 
   (mkdir-p (dirname cache-file))
   (call-with-output-file cache-file
@@ -454,14 +534,18 @@ optionally contain a version number and an output name, as in these examples:
   guile@2.0.9:debug
 
 If SPEC does not specify a version number, return the preferred newest
-version; if SPEC does not specify an output, return OUTPUT."
+version; if SPEC does not specify an output, return OUTPUT.
+
+When OUTPUT is false and SPEC does not specify any output, return #f as the
+output."
   (let-values (((name version sub-drv)
                 (package-specification->name+version+output spec output)))
     (match (%find-package spec name version)
       (#f
        (values #f #f))
       (package
-       (if (member sub-drv (package-outputs package))
+       (if (or (and (not output) (not sub-drv))
+               (member sub-drv (package-outputs package)))
            (values package sub-drv)
            (leave (G_ "package `~a' lacks output `~a'~%")
                   (package-full-name package)