guix: Separate the package name and version with "@", not "-".
[jackhill/guix/guix.git] / guix / packages.scm
index defde24..e0ab720 100644 (file)
@@ -1,8 +1,9 @@
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017 Ludovic Courtès <ludo@gnu.org>
-;;; Copyright © 2014, 2015 Mark H Weaver <mhw@netris.org>
+;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017, 2018 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2014, 2015, 2017 Mark H Weaver <mhw@netris.org>
 ;;; Copyright © 2015 Eric Bavier <bavier@member.fsf.org>
 ;;; Copyright © 2016 Alex Kost <alezost@gmail.com>
+;;; Copyright © 2017 Efraim Flashner <efraim@flashner.co.il>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -28,9 +29,9 @@
   #:use-module (guix base32)
   #:use-module (guix grafts)
   #:use-module (guix derivations)
+  #:use-module (guix memoization)
   #:use-module (guix build-system)
   #:use-module (guix search-paths)
-  #:use-module (guix gexp)
   #:use-module (guix sets)
   #:use-module (ice-9 match)
   #:use-module (ice-9 vlist)
             package-transitive-propagated-inputs
             package-transitive-native-search-paths
             package-transitive-supported-systems
+            package-mapping
             package-input-rewriting
             package-source-derivation
             package-derivation
             package-cross-derivation
             package-output
             package-grafts
+            package/inherit
 
             transitive-input-references
 
@@ -222,15 +225,18 @@ name of its URI."
 (define %supported-systems
   ;; This is the list of system types that are supported.  By default, we
   ;; expect all packages to build successfully here.
-  '("x86_64-linux" "i686-linux" "armhf-linux" "mips64el-linux"))
+  '("x86_64-linux" "i686-linux" "armhf-linux" "aarch64-linux" "mips64el-linux"))
 
 (define %hurd-systems
   ;; The GNU/Hurd systems for which support is being developed.
   '("i585-gnu" "i686-gnu"))
 
 (define %hydra-supported-systems
-  ;; This is the list of system types for which build slaves are available.
-  %supported-systems)
+  ;; This is the list of system types for which build machines are available.
+  ;;
+  ;; XXX: MIPS is temporarily unavailable on Hydra:
+  ;; <https://lists.gnu.org/archive/html/guix-devel/2017-03/msg00790.html>.
+  (fold delete %supported-systems '("aarch64-linux" "mips64el-linux")))
 
 
 ;; A package.
@@ -263,8 +269,11 @@ name of its URI."
                                                   ; inputs
   (native-search-paths package-native-search-paths (default '()))
   (search-paths package-search-paths (default '()))
+
+  ;; The 'replacement' field is marked as "innate" because it never makes
+  ;; sense to inherit a replacement as is.  See the 'package/inherit' macro.
   (replacement package-replacement                ; package | #f
-               (default #f) (thunked))
+               (default #f) (thunked) (innate))
 
   (synopsis package-synopsis)                    ; one-line description
   (description package-description)              ; one or two paragraphs
@@ -379,10 +388,11 @@ object."
 (define-condition-type &package-cross-build-system-error &package-error
   package-cross-build-system-error?)
 
-
-(define (package-full-name package)
-  "Return the full name of PACKAGE--i.e., `NAME-VERSION'."
-  (string-append (package-name package) "-" (package-version package)))
+(define* (package-full-name package #:optional (delimiter "@"))
+  "Return the full name of PACKAGE--i.e., `NAME@VERSION'.  By specifying
+DELIMITER (a string), you can customize what will appear between the name and
+the version.  By default, DELIMITER is \"@\"."
+  (string-append (package-name package) delimiter (package-version package)))
 
 (define (%standard-patch-inputs)
   (let* ((canonical (module-ref (resolve-interface '(gnu packages base))
@@ -395,7 +405,7 @@ object."
       ("bzip2" ,(ref '(gnu packages compression) 'bzip2))
       ("gzip"  ,(ref '(gnu packages compression) 'gzip))
       ("lzip"  ,(ref '(gnu packages compression) 'lzip))
-      ("unzip" ,(ref '(gnu packages zip) 'unzip))
+      ("unzip" ,(ref '(gnu packages compression) 'unzip))
       ("patch" ,(ref '(gnu packages base) 'patch))
       ("locales" ,(ref '(gnu packages base) 'glibc-utf8-locales)))))
 
@@ -405,6 +415,13 @@ derivations."
   (let ((distro (resolve-interface '(gnu packages commencement))))
     (module-ref distro 'guile-final)))
 
+(define (guile-2.0)
+  "Return Guile 2.0."
+  ;; FIXME: This is used as a workaround for <https://bugs.gnu.org/28211> when
+  ;; grafting packages.
+  (let ((distro (resolve-interface '(gnu packages guile))))
+    (module-ref distro 'guile-2.0)))
+
 (define* (default-guile-derivation #:optional (system (%current-system)))
   "Return the derivation for SYSTEM of the default Guile package used to run
 the build code of derivation."
@@ -471,10 +488,10 @@ specifies modules in scope when evaluating SNIPPET."
 
   (define instantiate-patch
     (match-lambda
-      ((? string? patch)
+      ((? string? patch)                          ;deprecated
        (interned-file patch #:recursive? #t))
-      ((? origin? patch)
-       (origin->derivation patch system))))
+      ((? struct? patch)                          ;origin, local-file, etc.
+       (lower-object patch system))))
 
   (mlet %store-monad ((tar ->     (lookup-input "tar"))
                       (xz ->      (lookup-input "xz"))
@@ -522,7 +539,8 @@ specifies modules in scope when evaluating SNIPPET."
               (setenv "LOCPATH"
                       (string-append #+locales "/lib/locale/"
                                      #+(and locales
-                                            (package-version locales))))
+                                            (version-major+minor
+                                             (package-version locales)))))
               (setlocale LC_ALL "en_US.utf8"))
 
             (setenv "PATH" (string-append #+xz "/bin" ":"
@@ -572,7 +590,12 @@ specifies modules in scope when evaluating SNIPPET."
                                                     #:fail-on-error? #t)))))
                         (zero? (apply system*
                                       (string-append #+tar "/bin/tar")
-                                      "cvfa" #$output
+                                      "cvf" #$output
+                                      ;; The bootstrap xz does not support
+                                      ;; threaded compression (introduced in
+                                      ;; 5.2.0), but it ignores the extra flag.
+                                      (string-append "--use-compress-program="
+                                                     #+xz "/bin/xz --threads=0")
                                       ;; avoid non-determinism in the archive
                                       "--mtime=@0"
                                       "--owner=root:0"
@@ -587,6 +610,7 @@ specifies modules in scope when evaluating SNIPPET."
       (gexp->derivation name build
                         #:graft? #f
                         #:system system
+                        #:deprecation-warnings #t ;to avoid a rebuild
                         #:guile-for-build guile-for-build))))
 
 (define (transitive-inputs inputs)
@@ -597,6 +621,9 @@ itself.
 This is implemented as a breadth-first traversal such that INPUTS is
 preserved, and only duplicate propagated inputs are removed."
   (define (seen? seen item outputs)
+    ;; FIXME: We're using pointer identity here, which is extremely sensitive
+    ;; to memoization in package-producing procedures; see
+    ;; <https://bugs.gnu.org/30155>.
     (match (vhash-assq item seen)
       ((_ . o) (equal? o outputs))
       (_       #f)))
@@ -697,38 +724,19 @@ in INPUTS and their transitive propagated inputs."
          `(assoc-ref ,alist ,(label input)))
        (transitive-inputs inputs)))
 
-(define-syntax define-memoized/v
-  (lambda (form)
-    "Define a memoized single-valued unary procedure with docstring.
-The procedure argument is compared to cached keys using `eqv?'."
-    (syntax-case form ()
-      ((_ (proc arg) docstring body body* ...)
-       (string? (syntax->datum #'docstring))
-       #'(define proc
-           (let ((cache (make-hash-table)))
-             (define (proc arg)
-               docstring
-               (match (hashv-get-handle cache arg)
-                 ((_ . value)
-                  value)
-                 (_
-                  (let ((result (let () body body* ...)))
-                    (hashv-set! cache arg result)
-                    result))))
-             proc))))))
-
-(define-memoized/v (package-transitive-supported-systems package)
-  "Return the intersection of the systems supported by PACKAGE and those
+(define package-transitive-supported-systems
+  (mlambdaq (package)
+    "Return the intersection of the systems supported by PACKAGE and those
 supported by its dependencies."
-  (fold (lambda (input systems)
-          (match input
-            ((label (? package? p) . _)
-             (lset-intersection
-              string=? systems (package-transitive-supported-systems p)))
-            (_
-             systems)))
-        (package-supported-systems package)
-        (bag-direct-inputs (package->bag package))))
+    (fold (lambda (input systems)
+            (match input
+              ((label (? package? p) . _)
+               (lset-intersection
+                string=? systems (package-transitive-supported-systems p)))
+              (_
+               systems)))
+          (package-supported-systems package)
+          (bag-direct-inputs (package->bag package)))))
 
 (define* (supported-package? package #:optional (system (%current-system)))
   "Return true if PACKAGE is supported on SYSTEM--i.e., if PACKAGE and all its
@@ -757,6 +765,36 @@ dependencies are known to build on SYSTEM."
   "Return the \"target inputs\" of BAG, recursively."
   (transitive-inputs (bag-target-inputs bag)))
 
+(define* (package-mapping proc #:optional (cut? (const #f)))
+  "Return a procedure that, given a package, applies PROC to all the packages
+depended on and returns the resulting package.  The procedure stops recursion
+when CUT? returns true for a given package."
+  (define (rewrite input)
+    (match input
+      ((label (? package? package) outputs ...)
+       (let ((proc (if (cut? package) proc replace)))
+         (cons* label (proc package) outputs)))
+      (_
+       input)))
+
+  (define replace
+    (mlambdaq (p)
+      ;; Return a variant of P with PROC applied to P and its explicit
+      ;; dependencies, recursively.  Memoize the transformations.  Failing to
+      ;; do that, we would build a huge object graph with lots of duplicates,
+      ;; which in turns prevents us from benefiting from memoization in
+      ;; 'package-derivation'.
+      (let ((p (proc p)))
+        (package
+          (inherit p)
+          (location (package-location p))
+          (inputs (map rewrite (package-inputs p)))
+          (native-inputs (map rewrite (package-native-inputs p)))
+          (propagated-inputs (map rewrite (package-propagated-inputs p)))
+          (replacement (and=> (package-replacement p) proc))))))
+
+  replace)
+
 (define* (package-input-rewriting replacements
                                   #:optional (rewrite-name identity))
   "Return a procedure that, when passed a package, replaces its direct and
@@ -766,25 +804,24 @@ package to replace, and the second one is the replacement.
 
 Optionally, REWRITE-NAME is a one-argument procedure that takes the name of a
 package and returns its new name after rewrite."
-  (define (rewrite input)
-    (match input
-      ((label (? package? package) outputs ...)
-       (match (assq-ref replacements package)
-         (#f  (cons* label (replace package) outputs))
-         (new (cons* label new outputs))))
-      (_
-       input)))
-
-  (define-memoized/v (replace p)
-    "Return a variant of P with its inputs rewritten."
-    (package
-      (inherit p)
-      (name (rewrite-name (package-name p)))
-      (inputs (map rewrite (package-inputs p)))
-      (native-inputs (map rewrite (package-native-inputs p)))
-      (propagated-inputs (map rewrite (package-propagated-inputs p)))))
-
-  replace)
+  (define (rewrite p)
+    (match (assq-ref replacements p)
+      (#f  (package
+             (inherit p)
+             (name (rewrite-name (package-name p)))))
+      (new new)))
+
+  (package-mapping rewrite (cut assq <> replacements)))
+
+(define-syntax-rule (package/inherit p overrides ...)
+  "Like (package (inherit P) OVERRIDES ...), except that the same
+transformation is done to the package replacement, if any.  P must be a bare
+identifier, and will be bound to either P or its replacement when evaluating
+OVERRIDES."
+  (let loop ((p p))
+    (package (inherit p)
+      overrides ...
+      (replacement (and=> (package-replacement p) loop)))))
 
 \f
 ;;;
@@ -860,7 +897,16 @@ information in exceptions."
      ;; source.
      (list name (intern file)))
     (((? string? name) (? struct? source))
-     (list name (package-source-derivation store source system)))
+     ;; 'package-source-derivation' calls 'lower-object', which can throw
+     ;; '&gexp-input-error'.  However '&gexp-input-error' lacks source
+     ;; location info, so we catch and rethrow here (XXX: not optimal
+     ;; performance-wise).
+     (guard (c ((gexp-input-error? c)
+                (raise (condition
+                        (&package-input-error
+                         (package package)
+                         (input   (gexp-error-invalid-input c)))))))
+       (list name (package-source-derivation store source system))))
     (x
      (raise (condition (&package-input-error
                         (package package)
@@ -890,6 +936,10 @@ and return it."
               (($ <package> name version source build-system
                             args inputs propagated-inputs native-inputs
                             self-native-input? outputs)
+               ;; Even though we prefer to use "@" to separate the package
+               ;; name from the package version in various user-facing parts
+               ;; of Guix, checkStoreName (in nix/libstore/store-api.cc)
+               ;; prohibits the use of "@", so use "-" instead.
                (or (make-bag build-system (string-append name "-" version)
                              #:system system
                              #:target target
@@ -956,14 +1006,18 @@ and return it."
   "Fold PROC over the packages BAG depends on.  Each package is visited only
 once, in depth-first order.  If NATIVE? is true, restrict to native
 dependencies; otherwise, restrict to target dependencies."
+  (define bag-direct-inputs*
+    (if native?
+        (lambda (bag)
+          (append (bag-build-inputs bag)
+                  (bag-target-inputs bag)
+                  (if (bag-target bag)
+                      '()
+                      (bag-host-inputs bag))))
+        bag-host-inputs))
+
   (define nodes
-    (match (if native?
-               (append (bag-build-inputs bag)
-                       (bag-target-inputs bag)
-                       (if (bag-target bag)
-                           '()
-                           (bag-host-inputs bag)))
-               (bag-host-inputs bag))
+    (match (bag-direct-inputs* bag)
       (((labels things _ ...) ...)
        things)))
 
@@ -976,7 +1030,7 @@ dependencies; otherwise, restrict to target dependencies."
       (((? package? head) . tail)
        (if (set-contains? visited head)
            (loop tail result visited)
-           (let ((inputs (bag-direct-inputs (package->bag head))))
+           (let ((inputs (bag-direct-inputs* (package->bag head))))
              (loop (match inputs
                      (((labels things _ ...) ...)
                       (append things tail)))
@@ -1113,7 +1167,7 @@ This is an internal procedure."
                   (()
                    drv)
                   (grafts
-                   (let ((guile (package-derivation store (default-guile)
+                   (let ((guile (package-derivation store (guile-2.0)
                                                     system #:graft? #f)))
                      ;; TODO: As an optimization, we can simply graft the tip
                      ;; of the derivation graph since 'graft-derivation'
@@ -1139,7 +1193,7 @@ system identifying string)."
                    (graft-derivation store drv grafts
                                      #:system system
                                      #:guile
-                                     (package-derivation store (default-guile)
+                                     (package-derivation store (guile-2.0)
                                                          system #:graft? #f))))
                 drv))))