1 ;;; Guix --- Nix package management from Guile. -*- coding: utf-8 -*-
2 ;;; Copyright (C) 2012 Ludovic Courtès <ludo@gnu.org>
4 ;;; This file is part of Guix.
6 ;;; Guix is free software; you can redistribute it and/or modify it
7 ;;; under the terms of the GNU General Public License as published by
8 ;;; the Free Software Foundation; either version 3 of the License, or (at
9 ;;; your option) any later version.
11 ;;; Guix is distributed in the hope that it will be useful, but
12 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ;;; GNU General Public License for more details.
16 ;;; You should have received a copy of the GNU General Public License
17 ;;; along with Guix. If not, see <http://www.gnu.org/licenses/>.
19 (define-module (guix derivations)
20 #:use-module (srfi srfi-1)
21 #:use-module (srfi srfi-9)
22 #:use-module (srfi srfi-26)
23 #:use-module (rnrs io ports)
24 #:use-module (rnrs bytevectors)
25 #:use-module (ice-9 match)
26 #:use-module (ice-9 rdelim)
27 #:use-module (guix store)
28 #:use-module (guix utils)
29 #:export (<derivation>
35 derivation-builder-arguments
36 derivation-builder-environment-vars
37 derivation-prerequisites
38 derivation-prerequisites-to-build
42 derivation-output-path
43 derivation-output-hash-algo
44 derivation-output-hash
49 derivation-input-sub-derivations
51 fixed-output-derivation?
56 derivation-path->output-path
60 build-expression->derivation
64 ;;; Nix derivations, as implemented in Nix's `derivations.cc'.
67 (define-record-type <derivation>
68 (make-derivation outputs inputs sources system builder args env-vars)
70 (outputs derivation-outputs) ; list of name/<derivation-output> pairs
71 (inputs derivation-inputs) ; list of <derivation-input>
72 (sources derivation-sources) ; list of store paths
73 (system derivation-system) ; string
74 (builder derivation-builder) ; store path
75 (args derivation-builder-arguments) ; list of strings
76 (env-vars derivation-builder-environment-vars)) ; list of name/value pairs
78 (define-record-type <derivation-output>
79 (make-derivation-output path hash-algo hash)
81 (path derivation-output-path) ; store path
82 (hash-algo derivation-output-hash-algo) ; symbol | #f
83 (hash derivation-output-hash)) ; bytevector | #f
85 (define-record-type <derivation-input>
86 (make-derivation-input path sub-derivations)
88 (path derivation-input-path) ; store path
89 (sub-derivations derivation-input-sub-derivations)) ; list of strings
91 (define (fixed-output-derivation? drv)
92 "Return #t if DRV is a fixed-output derivation, such as the result of a
93 download with a fixed hash (aka. `fetchurl')."
96 (($ <derivation-output> _ (? symbol?) (? string?))))
100 (define (derivation-prerequisites drv)
101 "Return the list of derivation-inputs required to build DRV, recursively."
104 (let ((inputs (remove (cut member <> result) ; XXX: quadratic
105 (derivation-inputs drv))))
107 (append inputs result)
109 (call-with-input-file (derivation-input-path i)
113 (define (derivation-prerequisites-to-build store drv)
114 "Return the list of derivation-inputs required to build DRV and not already
115 available in STORE, recursively."
118 (($ <derivation-input> path sub-drvs)
119 (let ((out (map (cut derivation-path->output-path path <>)
121 (any (cut valid-path? store <>) out)))))
125 (let ((inputs (remove (lambda (i)
126 (or (member i result) ; XXX: quadratic
128 (derivation-inputs drv))))
130 (append inputs result)
132 (call-with-input-file (derivation-input-path i)
136 (define (read-derivation drv-port)
137 "Read the derivation from DRV-PORT and return the corresponding
138 <derivation> object."
140 (define comma (string->symbol ","))
142 (define (ununquote x)
144 (('unquote x) (ununquote x))
145 ((x ...) (map ununquote x))
148 (define (outputs->alist x)
149 (fold-right (lambda (output result)
153 (make-derivation-output path #f #f)
155 ((name path hash-algo hash)
157 (let ((algo (string->symbol hash-algo))
158 (hash (base16-string->bytevector hash)))
160 (make-derivation-output path algo hash)
165 (define (make-input-drvs x)
166 (fold-right (lambda (input result)
168 ((path (sub-drvs ...))
169 (cons (make-derivation-input path sub-drvs)
174 (let loop ((exp (read drv-port))
178 (let ((result (reverse result)))
180 (('Derive ((outputs ...) (input-drvs ...)
184 ((? string? args) ...)
186 (make-derivation (outputs->alist outputs)
187 (make-input-drvs input-drvs)
190 (fold-right alist-cons '() var value)))
192 (error "failed to parse derivation" drv-port result)))))
193 ((? (cut eq? <> comma))
194 (loop (read drv-port) result))
196 (loop (read drv-port)
197 (cons (ununquote exp) result))))))
199 (define (write-derivation drv port)
200 "Write the ATerm-like serialization of DRV to PORT. See Section 2.4 of
201 Eelco Dolstra's PhD dissertation for an overview of a previous version of
203 (define (list->string lst)
204 (string-append "[" (string-join lst ",") "]"))
206 (define (write-list lst)
207 (display (list->string lst) port))
210 (($ <derivation> outputs inputs sources
211 system builder args env-vars)
212 (display "Derive(" port)
213 (write-list (map (match-lambda
214 ((name . ($ <derivation-output> path hash-algo hash))
215 (format #f "(~s,~s,~s,~s)"
217 (or (and=> hash-algo symbol->string) "")
218 (or (and=> hash bytevector->base16-string)
222 (write-list (map (match-lambda
223 (($ <derivation-input> path sub-drvs)
224 (format #f "(~s,~a)" path
225 (list->string (map object->string sub-drvs)))))
228 (write-list (map object->string sources))
229 (format port ",~s,~s," system builder)
230 (write-list (map object->string args))
232 (write-list (map (match-lambda
234 (format #f "(~s,~s)" name value)))
236 (display ")" port))))
238 (define* (derivation-path->output-path path #:optional (output "out"))
239 "Read the derivation from PATH (`/nix/store/xxx.drv'), and return the store
240 path of its output OUTPUT."
241 (let* ((drv (call-with-input-file path read-derivation))
242 (outputs (derivation-outputs drv)))
243 (and=> (assoc-ref outputs output) derivation-output-path)))
247 ;;; Derivation primitive.
250 (define (compressed-hash bv size) ; `compressHash'
251 "Given the hash stored in BV, return a compressed version thereof that fits
253 (define new (make-bytevector size 0))
254 (define old-size (bytevector-length bv))
258 (let* ((j (modulo i size))
259 (o (bytevector-u8-ref new j)))
260 (bytevector-u8-set! new j
261 (logxor o (bytevector-u8-ref bv i)))
264 (define derivation-hash ; `hashDerivationModulo' in derivations.cc
267 "Return the hash of DRV, modulo its fixed-output inputs, as a bytevector."
269 (($ <derivation> ((_ . ($ <derivation-output> path
270 (? symbol? hash-algo) (? bytevector? hash)))))
271 ;; A fixed-output derivation.
274 (string-append "fixed:out:" (symbol->string hash-algo)
275 ":" (bytevector->base16-string hash)
277 (($ <derivation> outputs inputs sources
278 system builder args env-vars)
279 ;; A regular derivation: replace the path of each input with that
280 ;; input's hash; return the hash of serialization of the resulting
281 ;; derivation. Note: inputs are sorted as in the order of their hex
282 ;; hash representation because that's what the C++ `std::map' code
284 (let* ((inputs (sort (map (match-lambda
285 (($ <derivation-input> path sub-drvs)
286 (let ((hash (call-with-input-file path
287 (compose bytevector->base16-string
290 (make-derivation-input hash sub-drvs))))
293 (string<? (derivation-input-path i1)
294 (derivation-input-path i2)))))
295 (sources (sort sources string<?))
296 (outputs (sort outputs
298 (string<? (car o1) (car o2)))))
299 (drv (make-derivation outputs inputs sources
300 system builder args env-vars)))
302 (string->utf8 (call-with-output-string
303 (cut write-derivation drv <>))))))))))
305 (define (store-path type hash name) ; makeStorePath
306 "Return the store path for NAME/HASH/TYPE."
307 (let* ((s (string-append type ":sha256:"
308 (bytevector->base16-string hash) ":"
309 (%store-prefix) ":" name))
310 (h (sha256 (string->utf8 s)))
311 (c (compressed-hash h 20)))
312 (string-append (%store-prefix) "/"
313 (bytevector->nix-base32-string c) "-"
316 (define (output-path output hash name) ; makeOutputPath
317 "Return an output path for OUTPUT (the name of the output as a string) of
318 the derivation called NAME with hash HASH."
319 (store-path (string-append "output:" output) hash
320 (if (string=? output "out")
322 (string-append name "-" output))))
324 (define* (derivation store name system builder args env-vars inputs
325 #:key (outputs '("out")) hash hash-algo hash-mode)
326 "Build a derivation with the given arguments. Return the resulting
327 store path and <derivation> object. When HASH, HASH-ALGO, and HASH-MODE
328 are given, a fixed-output derivation is created---i.e., one whose result is
329 known in advance, such as a file download."
330 (define (add-output-paths drv)
331 ;; Return DRV with an actual store path for each of its output and the
332 ;; corresponding environment variable.
334 (($ <derivation> outputs inputs sources
335 system builder args env-vars)
336 (let* ((drv-hash (derivation-hash drv))
337 (outputs (map (match-lambda
338 ((output-name . ($ <derivation-output>
340 (let ((path (output-path output-name
343 (make-derivation-output path algo
346 (make-derivation outputs inputs sources system builder args
350 (or (and=> (assoc-ref outputs name)
351 derivation-output-path)
355 (define (env-vars-with-empty-outputs)
356 ;; Return a variant of ENV-VARS where each OUTPUTS is associated with an
357 ;; empty string, even outputs that do not appear in ENV-VARS. Note: the
358 ;; result is sorted alphabetically, as with C++ `std::map'.
359 (let ((e (map (match-lambda
361 (if (member name outputs)
365 (sort (fold (lambda (output-name env-vars)
366 (if (assoc output-name env-vars)
368 (append env-vars `((,output-name . "")))))
372 (string<? (car e1) (car e2))))))
374 (let* ((outputs (map (lambda (name)
375 ;; Return outputs with an empty path.
377 (make-derivation-output "" hash-algo hash)))
379 (inputs (map (match-lambda
380 (((? store-path? input))
381 (make-derivation-input input '("out")))
382 (((? store-path? input) sub-drvs ...)
383 (make-derivation-input input sub-drvs))
385 (let ((path (add-to-store store
387 (hash-algo sha256) #t #t
389 (make-derivation-input path '()))))
390 (delete-duplicates inputs)))
391 (env-vars (env-vars-with-empty-outputs))
392 (drv-masked (make-derivation outputs
393 (filter (compose derivation-path?
394 derivation-input-path)
396 (filter-map (lambda (i)
397 (let ((p (derivation-input-path i)))
398 (and (not (derivation-path? p))
401 system builder args env-vars))
402 (drv (add-output-paths drv-masked)))
404 (values (add-text-to-store store (string-append name ".drv")
405 (call-with-output-string
406 (cut write-derivation drv <>))
407 (map derivation-input-path
413 ;;; Guile-based builders.
416 (define %guile-for-build
417 ;; The derivation of the Guile to be used within the build environment,
418 ;; when using `build-expression->derivation'.
419 (make-parameter (false-if-exception (nixpkgs-derivation "guile"))))
421 (define* (imported-files store files
422 #:key (name "file-import") (system (%current-system)))
423 "Return a derivation that imports FILES into STORE. FILES must be a list
424 of (FINAL-PATH . FILE-NAME) pairs; each FILE-NAME is read from the file
425 system, imported, and appears under FINAL-PATH in the resulting store path."
426 (define (parent-dirs file-name)
427 ;; Return the list of parent dirs of FILE-NAME, in the order in which an
428 ;; `mkdir -p' implementation would make them.
429 (let ((not-slash (char-set-complement (char-set #\/))))
431 (fold (lambda (dir result)
436 (cons (string-append prev "/" dir)
439 (remove (cut string=? <> ".")
440 (string-tokenize (dirname file-name) not-slash))))))
442 (let* ((files (map (match-lambda
443 ((final-path . file-name)
445 (add-to-store store (basename final-path) #t #f
446 "sha256" file-name))))
450 (mkdir %output) (chdir %output)
451 ,@(append-map (match-lambda
452 ((final-path store-path)
453 (append (match (parent-dirs final-path)
456 (append (map (lambda (d)
460 `((or (file-exists? ,tail)
462 `((symlink ,store-path ,final-path)))))
464 (build-expression->derivation store name (%current-system)
467 (define* (imported-modules store modules
468 #:key (name "module-import")
469 (system (%current-system)))
470 "Return a derivation that contains the source files of MODULES, a list of
471 module names such as `(ice-9 q)'. All of MODULES must be in the current
473 ;; TODO: Determine the closure of MODULES, build the `.go' files,
474 ;; canonicalize the source files through read/write, etc.
475 (let ((files (map (lambda (m)
476 (let ((f (string-append
477 (string-join (map symbol->string m) "/")
479 (cons f (search-path %load-path f))))
481 (imported-files store files #:name name #:system system)))
484 (define* (build-expression->derivation store name system exp inputs
485 #:key (outputs '("out"))
488 "Return a derivation that executes Scheme expression EXP as a builder for
489 derivation NAME. INPUTS must be a list of (NAME DRV-PATH SUB-DRV) tuples;
490 when SUB-DRV is omitted, \"out\" is assumed. EXP is evaluated in an
491 environment where %OUTPUT is bound to the main output path, %OUTPUTS is bound
492 to a list of output/path pairs, and where %BUILD-INPUTS is bound to an alist
493 of string/output-path pairs made from INPUTS. The builder terminates by
494 passing the result of EXP to `exit'; thus, when EXP returns #f, the build is
495 considered to have failed."
497 (string-append (derivation-path->output-path (%guile-for-build))
502 (((or 'define-module 'use-modules) _ ...) #t)
505 (let* ((prologue `(begin
508 ;; Module forms must appear at the top-level so
509 ;; that any macros they export can be expanded.
510 (filter module-form? exp))
513 (define %output (getenv "out"))
518 (define %build-inputs
521 (let ((sub (match rest
525 (if (derivation-path? drv)
526 (derivation-path->output-path drv
530 (builder (add-text-to-store store
531 (string-append name "-guile-builder")
533 (object->string prologue)
538 (remove module-form? exp))
540 (map second inputs)))
541 (mod-drv (if (null? modules)
543 (imported-modules store modules)))
544 (mod-dir (and mod-drv
545 (derivation-path->output-path mod-drv))))
546 (derivation store name system guile
547 `("--no-auto-compile"
548 ,@(if mod-dir `("-L" ,mod-dir) '())
550 '(("HOME" . "/homeless"))
551 `((,(%guile-for-build))
554 ,@(if mod-drv `((,mod-drv)) '()))
555 #:hash hash #:hash-algo hash-algo