packages: Add `native-search-paths' field and honor it.
[jackhill/guix/guix.git] / guix / packages.scm
dissimilarity index 64%
index 00751ce..3a6a07b 100644 (file)
-;;; Guix --- Nix package management from Guile.         -*- coding: utf-8 -*-
-;;; Copyright (C) 2012 Ludovic Courtès <ludo@gnu.org>
-;;;
-;;; This file is part of Guix.
-;;;
-;;; 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.
-;;;
-;;; 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 Guix.  If not, see <http://www.gnu.org/licenses/>.
-
-(define-module (guix packages)
-  #:use-module (guix utils)
-  #:use-module (guix store)
-  #:use-module (guix build-system)
-  #:use-module (ice-9 match)
-  #:use-module (srfi srfi-9)
-  #:export (location
-            location?
-            location-file
-            location-line
-            location-column
-
-            source
-            package-source?
-            package-source-uri
-            package-source-method
-            package-source-sha256
-            package-source-file-name
-
-            package
-            package?
-            package-name
-            package-version
-            package-source
-            package-build-system
-            package-arguments
-            package-inputs
-            package-native-inputs
-            package-outputs
-            package-search-paths
-            package-description
-            package-long-description
-            package-license
-            package-platforms
-            package-maintainers
-            package-location
-
-            package-source-derivation
-            package-derivation
-            package-cross-derivation))
-
-;;; Commentary:
-;;;
-;;; This module provides a high-level mechanism to define packages in a
-;;; Guix-based distribution.
-;;;
-;;; Code:
-
-;; A source location.
-(define-record-type <location>
-  (make-location file line column)
-  location?
-  (file          location-file)                   ; file name
-  (line          location-line)                   ; 1-indexed line
-  (column        location-column))                ; 0-indexed column
-
-(define location
-  (memoize
-   (lambda (file line column)
-     "Return the <location> object for the given FILE, LINE, and COLUMN."
-     (and line column file
-          (make-location file line column)))))
-
-(define (source-properties->location loc)
-  "Return a location object based on the info in LOC, an alist as returned
-by Guile's `source-properties', `frame-source', `current-source-location',
-etc."
-  (let ((file (assq-ref loc 'filename))
-        (line (assq-ref loc 'line))
-        (col  (assq-ref loc 'column)))
-    (location file (and line (+ line 1)) col)))
-
-
-;; The source of a package, such as a tarball URL and fetcher.
-(define-record-type* <package-source>
-  source make-package-source
-  package-source?
-  (uri       package-source-uri)                     ; string
-  (method    package-source-method)                  ; symbol
-  (sha256    package-source-sha256)                  ; bytevector
-  (file-name package-source-file-name                ; optional file name
-             (default #f)))
-
-;; A package.
-(define-record-type* <package>
-  package make-package
-  package?
-  (name   package-name)                   ; string
-  (version package-version)               ; string
-  (source package-source)                 ; <package-source> instance
-  (build-system package-build-system)     ; build system
-  (arguments package-arguments)           ; arguments for the build method
-  (inputs package-inputs                  ; input packages or derivations
-          (default '()))
-  (native-inputs package-native-inputs    ; native input packages/derivations
-                 (default '()))
-  (outputs package-outputs                ; list of strings
-           (default '("out")))
-  (search-paths package-search-paths      ; list of (ENV-VAR (DIRS ...))
-                (default '()))            ; tuples; see
-                                          ; `set-path-environment-variable'
-                                          ; (aka. "setup-hook")
-
-  (description package-description)       ; one-line description
-  (long-description package-long-description)     ; one or two paragraphs
-  (license package-license (default '()))
-  (platforms package-platforms (default '()))
-  (maintainers package-maintainers (default '()))
-  (location package-location
-            (default (and=> (current-source-location)
-                            source-properties->location))))
-
-(define (package-source-derivation store source)
-  "Return the derivation path for SOURCE, a package source."
-  (match source
-    (($ <package-source> uri method sha256 name)
-     (method store uri 'sha256 sha256 name))))
-
-(define* (package-derivation store package
-                             #:optional (system (%current-system)))
-  "Return the derivation of PACKAGE for SYSTEM."
-  (match package
-    (($ <package> name version source (= build-system-builder builder)
-        args inputs native-inputs outputs)
-     ;; TODO: For `search-paths', add a builder prologue that calls
-     ;; `set-path-environment-variable'.
-     (let ((inputs (map (match-lambda
-                         (((? string? name) (and package ($ <package>)))
-                          (list name (package-derivation store package)))
-                         (((? string? name) (and package ($ <package>))
-                           (? string? sub-drv))
-                          (list name (package-derivation store package)
-                                sub-drv))
-                         (((? string? name)
-                           (and (? string?) (? derivation-path?) drv))
-                          (list name drv)))
-                        (append native-inputs inputs))))
-       (apply builder
-              store (string-append name "-" version)
-              (package-source-derivation store source)
-              inputs
-              #:outputs outputs #:system system
-              args)))))
-
-(define* (package-cross-derivation store package)
-  ;; TODO
-  #f)
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2012, 2013 Ludovic Courtès <ludo@gnu.org>
+;;;
+;;; 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 packages)
+  #:use-module (guix utils)
+  #:use-module (guix store)
+  #:use-module (guix base32)
+  #:use-module (guix derivations)
+  #:use-module (guix build-system)
+  #:use-module (ice-9 match)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-9 gnu)
+  #:use-module (srfi srfi-11)
+  #:use-module (srfi srfi-34)
+  #:use-module (srfi srfi-35)
+  #:re-export (%current-system)
+  #:export (origin
+            origin?
+            origin-uri
+            origin-method
+            origin-sha256
+            origin-file-name
+            base32
+
+            <search-path-specification>
+            search-path-specification
+            search-path-specification?
+            search-path-specification->sexp
+
+            package
+            package?
+            package-name
+            package-version
+            package-full-name
+            package-source
+            package-build-system
+            package-arguments
+            package-inputs
+            package-native-inputs
+            package-propagated-inputs
+            package-outputs
+            package-native-search-paths
+            package-search-paths
+            package-synopsis
+            package-description
+            package-license
+            package-home-page
+            package-platforms
+            package-maintainers
+            package-properties
+            package-location
+
+            package-transitive-inputs
+            package-transitive-propagated-inputs
+            package-source-derivation
+            package-derivation
+            package-cross-derivation
+            package-output
+
+            &package-error
+            package-error?
+            package-error-package
+            &package-input-error
+            package-input-error?
+            package-error-invalid-input))
+
+;;; Commentary:
+;;;
+;;; This module provides a high-level mechanism to define packages in a
+;;; Guix-based distribution.
+;;;
+;;; Code:
+
+;; The source of a package, such as a tarball URL and fetcher---called
+;; "origin" to avoid name clash with `package-source', `source', etc.
+(define-record-type* <origin>
+  origin make-origin
+  origin?
+  (uri       origin-uri)                          ; string
+  (method    origin-method)                       ; symbol
+  (sha256    origin-sha256)                       ; bytevector
+  (file-name origin-file-name (default #f)))      ; optional file name
+
+(define-syntax base32
+  (lambda (s)
+    "Return the bytevector corresponding to the given Nix-base32
+representation."
+    (syntax-case s ()
+      ((_ str)
+       (string? (syntax->datum #'str))
+       ;; A literal string: do the conversion at expansion time.
+       (with-syntax ((bv (nix-base32-string->bytevector
+                          (syntax->datum #'str))))
+         #''bv))
+      ((_ str)
+       #'(nix-base32-string->bytevector str)))))
+
+;; The specification of a search path.
+(define-record-type* <search-path-specification>
+  search-path-specification make-search-path-specification
+  search-path-specification?
+  (variable     search-path-specification-variable)
+  (directories  search-path-specification-directories)
+  (separator    search-path-specification-separator (default ":")))
+
+(define (search-path-specification->sexp spec)
+  "Return an sexp representing SPEC, a <search-path-specification>.  The sexp
+corresponds to the arguments expected by `set-path-environment-variable'."
+  (match spec
+    (($ <search-path-specification> variable directories separator)
+     `(,variable ,directories ,separator))))
+
+;; A package.
+(define-record-type* <package>
+  package make-package
+  package?
+  (name   package-name)                   ; string
+  (version package-version)               ; string
+  (source package-source)                 ; <origin> instance
+  (build-system package-build-system)     ; build system
+  (arguments package-arguments            ; arguments for the build method
+             (default '()) (thunked))
+
+  (inputs package-inputs                  ; input packages or derivations
+          (default '()) (thunked))
+  (propagated-inputs package-propagated-inputs    ; same, but propagated
+                     (default '()))
+  (native-inputs package-native-inputs    ; native input packages/derivations
+                 (default '()))
+  (self-native-input? package-self-native-input?  ; whether to use itself as
+                                                  ; a native input when cross-
+                      (default #f))               ; compiling
+
+  (outputs package-outputs                ; list of strings
+           (default '("out")))
+
+                                                  ; lists of
+                                                  ; <search-path-specification>,
+                                                  ; for native and cross
+                                                  ; inputs
+  (native-search-paths package-native-search-paths (default '()))
+  (search-paths package-search-paths (default '()))
+
+  (synopsis package-synopsis)                    ; one-line description
+  (description package-description)              ; one or two paragraphs
+  (license package-license)
+  (home-page package-home-page)
+  (platforms package-platforms (default '()))
+  (maintainers package-maintainers (default '()))
+
+  (properties package-properties (default '()))   ; alist for anything else
+
+  (location package-location
+            (default (and=> (current-source-location)
+                            source-properties->location))))
+
+(set-record-type-printer! <package>
+                          (lambda (package port)
+                            (let ((loc    (package-location package))
+                                  (format simple-format))
+                              (format port "#<package ~a-~a ~a:~a ~a>"
+                                      (package-name package)
+                                      (package-version package)
+                                      (location-file loc)
+                                      (location-line loc)
+                                      (number->string (object-address
+                                                       package)
+                                                      16)))))
+
+
+;; Error conditions.
+
+(define-condition-type &package-error &error
+  package-error?
+  (package package-error-package))
+
+(define-condition-type &package-input-error &package-error
+  package-input-error?
+  (input package-error-invalid-input))
+
+
+(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-source-derivation store source
+                                    #:optional (system (%current-system)))
+  "Return the derivation path for SOURCE, a package source, for SYSTEM."
+  (match source
+    (($ <origin> uri method sha256 name)
+     (method store uri 'sha256 sha256 name
+             #:system system))))
+
+(define (transitive-inputs inputs)
+  (let loop ((inputs  inputs)
+             (result '()))
+    (match inputs
+      (()
+       (delete-duplicates (reverse result)))      ; XXX: efficiency
+      (((and i (name (? package? p) sub ...)) rest ...)
+       (let ((t (map (match-lambda
+                      ((dep-name derivation ...)
+                       (cons (string-append name "/" dep-name)
+                             derivation)))
+                     (package-propagated-inputs p))))
+         (loop (append t rest)
+               (append t (cons i result)))))
+      ((input rest ...)
+       (loop rest (cons input result))))))
+
+(define (package-transitive-inputs package)
+  "Return the transitive inputs of PACKAGE---i.e., its direct inputs along
+with their propagated inputs, recursively."
+  (transitive-inputs (append (package-native-inputs package)
+                             (package-inputs package)
+                             (package-propagated-inputs package))))
+
+(define (package-transitive-propagated-inputs package)
+  "Return the propagated inputs of PACKAGE, and their propagated inputs,
+recursively."
+  (transitive-inputs (package-propagated-inputs package)))
+
+\f
+;;;
+;;; Package derivations.
+;;;
+
+(define %derivation-cache
+  ;; Package to derivation-path mapping.
+  (make-weak-key-hash-table 100))
+
+(define (cache package system thunk)
+  "Memoize the return values of THUNK as the derivation of PACKAGE on
+SYSTEM."
+  (let ((vals (call-with-values thunk list)))
+    ;; Use `hashq-set!' instead of `hash-set!' because `hash' returns the
+    ;; same value for all structs (as of Guile 2.0.6), and because pointer
+    ;; equality is sufficient in practice.
+    (hashq-set! %derivation-cache package `((,system ,@vals)))
+    (apply values vals)))
+
+(define-syntax-rule (cached package system body ...)
+  "Memoize the result of BODY for the arguments PACKAGE and SYSTEM.
+Return the cached result when available."
+  (let ((thunk (lambda () body ...)))
+    (match (hashq-ref %derivation-cache package)
+      ((alist (... ...))
+       (match (assoc-ref alist system)
+         ((vals (... ...))
+          (apply values vals))
+         (#f
+          (cache package system thunk))))
+      (#f
+       (cache package system thunk)))))
+
+(define* (package-derivation store package
+                             #:optional (system (%current-system)))
+  "Return the derivation path and corresponding <derivation> object of
+PACKAGE for SYSTEM."
+  (define (intern file)
+    ;; Add FILE to the store.  Set the `recursive?' bit to #t, so that
+    ;; file permissions are preserved.
+    (add-to-store store (basename file) #t "sha256" file))
+
+  (define expand-input
+    ;; Expand the given input tuple such that it contains only
+    ;; references to derivation paths or store paths.
+    (match-lambda
+     (((? string? name) (? package? package))
+      (list name (package-derivation store package system)))
+     (((? string? name) (? package? package)
+       (? string? sub-drv))
+      (list name (package-derivation store package system)
+            sub-drv))
+     (((? string? name)
+       (and (? string?) (? derivation-path?) drv))
+      (list name drv))
+     (((? string? name)
+       (and (? string?) (? file-exists? file)))
+      ;; Add FILE to the store.  When FILE is in the sub-directory of a
+      ;; store path, it needs to be added anyway, so it can be used as a
+      ;; source.
+      (list name (intern file)))
+     (((? string? name) (? origin? source))
+      (list name (package-source-derivation store source system)))
+     (x
+      (raise (condition (&package-input-error
+                         (package package)
+                         (input   x)))))))
+
+  ;; Compute the derivation and cache the result.  Caching is important
+  ;; because some derivations, such as the implicit inputs of the GNU build
+  ;; system, will be queried many, many times in a row.
+  (cached package system
+
+          ;; Bind %CURRENT-SYSTEM so that thunked field values can refer
+          ;; to it.
+          (parameterize ((%current-system system))
+            (match package
+              (($ <package> name version source (= build-system-builder builder)
+                  args inputs propagated-inputs native-inputs self-native-input?
+                  outputs)
+               (let* ((inputs     (package-transitive-inputs package))
+                      (input-drvs (map expand-input inputs))
+                      (paths      (delete-duplicates
+                                   (append-map (match-lambda
+                                                ((_ (? package? p) _ ...)
+                                                 (package-native-search-paths
+                                                  p))
+                                                (_ '()))
+                                               inputs))))
+
+                 (apply builder
+                        store (package-full-name package)
+                        (and source
+                             (package-source-derivation store source system))
+                        input-drvs
+                        #:search-paths paths
+                        #:outputs outputs #:system system
+                        (args))))))))
+
+(define* (package-cross-derivation store package)
+  ;; TODO
+  #f)
+
+(define* (package-output store package output
+                         #:optional (system (%current-system)))
+  "Return the output path of PACKAGE's OUTPUT for SYSTEM---where OUTPUT is the
+symbolic output name, such as \"out\".  Note that this procedure calls
+`package-derivation', which is costly."
+  (let-values (((_ drv)
+                (package-derivation store package system)))
+    (derivation-output-path
+     (assoc-ref (derivation-outputs drv) output))))