gnu: Add r-txdb-hsapiens-ucsc-hg19-knowngene.
[jackhill/guix/guix.git] / guix / profiles.scm
CommitLineData
cc4ecc2d 1;;; GNU Guix --- Functional package management for GNU
e5f04c2d 2;;; Copyright © 2013, 2014, 2015, 2016 Ludovic Courtès <ludo@gnu.org>
cc4ecc2d 3;;; Copyright © 2013 Nikita Karetnikov <nikita@karetnikov.org>
9eb5a449 4;;; Copyright © 2014, 2016 Alex Kost <alezost@gmail.com>
536c3ee4 5;;; Copyright © 2015 Mark H Weaver <mhw@netris.org>
b04af0ec 6;;; Copyright © 2015 Sou Bunnbu <iyzsong@gmail.com>
cc4ecc2d
LC
7;;;
8;;; This file is part of GNU Guix.
9;;;
10;;; GNU Guix is free software; you can redistribute it and/or modify it
11;;; under the terms of the GNU General Public License as published by
12;;; the Free Software Foundation; either version 3 of the License, or (at
13;;; your option) any later version.
14;;;
15;;; GNU Guix is distributed in the hope that it will be useful, but
16;;; WITHOUT ANY WARRANTY; without even the implied warranty of
17;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;;; GNU General Public License for more details.
19;;;
20;;; You should have received a copy of the GNU General Public License
21;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
22
23(define-module (guix profiles)
97425486
LC
24 #:use-module ((guix utils) #:hide (package-name->name+version))
25 #:use-module ((guix build utils)
26 #:select (package-name->name+version))
cc4ecc2d 27 #:use-module (guix records)
cc4ecc2d 28 #:use-module (guix packages)
e89431bf
LC
29 #:use-module (guix derivations)
30 #:use-module (guix search-paths)
a54c94a4 31 #:use-module (guix gexp)
79ee406d 32 #:use-module (guix monads)
e87f0591 33 #:use-module (guix store)
cc4ecc2d
LC
34 #:use-module (ice-9 match)
35 #:use-module (ice-9 regex)
36 #:use-module (ice-9 ftw)
343745c8 37 #:use-module (ice-9 format)
cc4ecc2d
LC
38 #:use-module (srfi srfi-1)
39 #:use-module (srfi srfi-9)
79601521 40 #:use-module (srfi srfi-11)
cc4ecc2d
LC
41 #:use-module (srfi srfi-19)
42 #:use-module (srfi srfi-26)
c0c018f1
AK
43 #:use-module (srfi srfi-34)
44 #:use-module (srfi srfi-35)
45 #:export (&profile-error
46 profile-error?
47 profile-error-profile
48 &profile-not-found-error
49 profile-not-found-error?
50 &missing-generation-error
51 missing-generation-error?
52 missing-generation-error-generation
53
54 manifest make-manifest
cc4ecc2d
LC
55 manifest?
56 manifest-entries
57
58 <manifest-entry> ; FIXME: eventually make it internal
59 manifest-entry
60 manifest-entry?
61 manifest-entry-name
62 manifest-entry-version
63 manifest-entry-output
a54c94a4 64 manifest-entry-item
cc4ecc2d 65 manifest-entry-dependencies
dedb17ad 66 manifest-entry-search-paths
cc4ecc2d 67
a2078770
LC
68 manifest-pattern
69 manifest-pattern?
03763d64
LC
70 manifest-pattern-name
71 manifest-pattern-version
72 manifest-pattern-output
a2078770 73
cc4ecc2d 74 manifest-remove
f7554030 75 manifest-add
ef8993e2 76 manifest-lookup
cc4ecc2d 77 manifest-installed?
a2078770 78 manifest-matching-entries
cc4ecc2d 79
343745c8
AK
80 manifest-transaction
81 manifest-transaction?
82 manifest-transaction-install
83 manifest-transaction-remove
c8c25704
LC
84 manifest-transaction-install-entry
85 manifest-transaction-remove-pattern
86 manifest-transaction-null?
343745c8 87 manifest-perform-transaction
79601521 88 manifest-transaction-effects
343745c8 89
cc4ecc2d 90 profile-manifest
462f5cca 91 package->manifest-entry
8404ed5c 92 packages->manifest
aa46a028 93 %default-profile-hooks
cc4ecc2d 94 profile-derivation
06d45f45 95
cc4ecc2d
LC
96 generation-number
97 generation-numbers
f452e8ff 98 profile-generations
3ccde087 99 relative-generation
cc4ecc2d
LC
100 previous-generation-number
101 generation-time
06d45f45
LC
102 generation-file-name
103 switch-to-generation
104 roll-back
105 delete-generation))
cc4ecc2d
LC
106
107;;; Commentary:
108;;;
109;;; Tools to create and manipulate profiles---i.e., the representation of a
110;;; set of installed packages.
111;;;
112;;; Code:
113
114\f
c0c018f1
AK
115;;;
116;;; Condition types.
117;;;
118
119(define-condition-type &profile-error &error
120 profile-error?
121 (profile profile-error-profile))
122
123(define-condition-type &profile-not-found-error &profile-error
124 profile-not-found-error?)
125
126(define-condition-type &missing-generation-error &profile-error
127 missing-generation-error?
128 (generation missing-generation-error-generation))
129
130\f
cc4ecc2d
LC
131;;;
132;;; Manifests.
133;;;
134
135(define-record-type <manifest>
136 (manifest entries)
137 manifest?
138 (entries manifest-entries)) ; list of <manifest-entry>
139
140;; Convenient alias, to avoid name clashes.
141(define make-manifest manifest)
142
143(define-record-type* <manifest-entry> manifest-entry
144 make-manifest-entry
145 manifest-entry?
146 (name manifest-entry-name) ; string
147 (version manifest-entry-version) ; string
148 (output manifest-entry-output ; string
149 (default "out"))
a54c94a4 150 (item manifest-entry-item) ; package | store path
4ca0b410 151 (dependencies manifest-entry-dependencies ; (store path | package)*
dedb17ad
LC
152 (default '()))
153 (search-paths manifest-entry-search-paths ; search-path-specification*
4ca0b410 154 (default '())))
cc4ecc2d 155
a2078770
LC
156(define-record-type* <manifest-pattern> manifest-pattern
157 make-manifest-pattern
158 manifest-pattern?
159 (name manifest-pattern-name) ; string
160 (version manifest-pattern-version ; string | #f
161 (default #f))
162 (output manifest-pattern-output ; string | #f
163 (default "out")))
164
cc4ecc2d
LC
165(define (profile-manifest profile)
166 "Return the PROFILE's manifest."
167 (let ((file (string-append profile "/manifest")))
168 (if (file-exists? file)
169 (call-with-input-file file read-manifest)
170 (manifest '()))))
171
9e90fc77
LC
172(define* (package->manifest-entry package #:optional (output "out"))
173 "Return a manifest entry for the OUTPUT of package PACKAGE."
462f5cca
LC
174 (let ((deps (map (match-lambda
175 ((label package)
b4a4bec0 176 (gexp-input package))
462f5cca 177 ((label package output)
b4a4bec0 178 (gexp-input package output)))
462f5cca
LC
179 (package-transitive-propagated-inputs package))))
180 (manifest-entry
181 (name (package-name package))
182 (version (package-version package))
9e90fc77 183 (output output)
462f5cca 184 (item package)
dedb17ad 185 (dependencies (delete-duplicates deps))
ccda8f7d 186 (search-paths (package-transitive-native-search-paths package)))))
462f5cca 187
8404ed5c
DT
188(define (packages->manifest packages)
189 "Return a list of manifest entries, one for each item listed in PACKAGES.
190Elements of PACKAGES can be either package objects or package/string tuples
191denoting a specific output of a package."
192 (manifest
193 (map (match-lambda
194 ((package output)
195 (package->manifest-entry package output))
9e90fc77
LC
196 ((? package? package)
197 (package->manifest-entry package)))
8404ed5c
DT
198 packages)))
199
a54c94a4
LC
200(define (manifest->gexp manifest)
201 "Return a representation of MANIFEST as a gexp."
202 (define (entry->gexp entry)
cc4ecc2d 203 (match entry
dedb17ad
LC
204 (($ <manifest-entry> name version output (? string? path)
205 (deps ...) (search-paths ...))
206 #~(#$name #$version #$output #$path
207 (propagated-inputs #$deps)
208 (search-paths #$(map search-path-specification->sexp
209 search-paths))))
210 (($ <manifest-entry> name version output (? package? package)
211 (deps ...) (search-paths ...))
a54c94a4 212 #~(#$name #$version #$output
dedb17ad
LC
213 (ungexp package (or output "out"))
214 (propagated-inputs #$deps)
215 (search-paths #$(map search-path-specification->sexp
216 search-paths))))))
cc4ecc2d
LC
217
218 (match manifest
219 (($ <manifest> (entries ...))
dedb17ad 220 #~(manifest (version 2)
a54c94a4 221 (packages #$(map entry->gexp entries))))))
cc4ecc2d 222
dedb17ad
LC
223(define (find-package name version)
224 "Return a package from the distro matching NAME and possibly VERSION. This
225procedure is here for backward-compatibility and will eventually vanish."
226 (define find-best-packages-by-name ;break abstractions
227 (module-ref (resolve-interface '(gnu packages))
228 'find-best-packages-by-name))
229
230 ;; Use 'find-best-packages-by-name' and not 'find-packages-by-name'; the
231 ;; former traverses the module tree only once and then allows for efficient
232 ;; access via a vhash.
233 (match (find-best-packages-by-name name version)
234 ((p _ ...) p)
235 (_
236 (match (find-best-packages-by-name name #f)
237 ((p _ ...) p)
238 (_ #f)))))
239
cc4ecc2d
LC
240(define (sexp->manifest sexp)
241 "Parse SEXP as a manifest."
dedb17ad
LC
242 (define (infer-search-paths name version)
243 ;; Infer the search path specifications for NAME-VERSION by looking up a
244 ;; same-named package in the distro. Useful for the old manifest formats
245 ;; that did not store search path info.
246 (let ((package (find-package name version)))
247 (if package
248 (package-native-search-paths package)
249 '())))
250
cc4ecc2d
LC
251 (match sexp
252 (('manifest ('version 0)
253 ('packages ((name version output path) ...)))
254 (manifest
255 (map (lambda (name version output path)
256 (manifest-entry
257 (name name)
258 (version version)
259 (output output)
dedb17ad
LC
260 (item path)
261 (search-paths (infer-search-paths name version))))
cc4ecc2d
LC
262 name version output path)))
263
264 ;; Version 1 adds a list of propagated inputs to the
265 ;; name/version/output/path tuples.
266 (('manifest ('version 1)
267 ('packages ((name version output path deps) ...)))
268 (manifest
269 (map (lambda (name version output path deps)
d34736c5
LC
270 ;; Up to Guix 0.7 included, dependencies were listed as ("gmp"
271 ;; "/gnu/store/...-gmp") for instance. Discard the 'label' in
272 ;; such lists.
273 (let ((deps (match deps
274 (((labels directories) ...)
275 directories)
276 ((directories ...)
277 directories))))
278 (manifest-entry
279 (name name)
280 (version version)
281 (output output)
282 (item path)
dedb17ad
LC
283 (dependencies deps)
284 (search-paths (infer-search-paths name version)))))
cc4ecc2d
LC
285 name version output path deps)))
286
dedb17ad
LC
287 ;; Version 2 adds search paths and is slightly more verbose.
288 (('manifest ('version 2 minor-version ...)
289 ('packages ((name version output path
290 ('propagated-inputs deps)
291 ('search-paths search-paths)
292 extra-stuff ...)
293 ...)))
294 (manifest
295 (map (lambda (name version output path deps search-paths)
296 (manifest-entry
297 (name name)
298 (version version)
299 (output output)
300 (item path)
301 (dependencies deps)
302 (search-paths (map sexp->search-path-specification
303 search-paths))))
304 name version output path deps search-paths)))
cc4ecc2d 305 (_
88aab8e3
LC
306 (raise (condition
307 (&message (message "unsupported manifest format")))))))
cc4ecc2d
LC
308
309(define (read-manifest port)
310 "Return the packages listed in MANIFEST."
311 (sexp->manifest (read port)))
312
a2078770
LC
313(define (entry-predicate pattern)
314 "Return a procedure that returns #t when passed a manifest entry that
315matches NAME/OUTPUT/VERSION. OUTPUT and VERSION may be #f, in which case they
316are ignored."
317 (match pattern
318 (($ <manifest-pattern> name version output)
319 (match-lambda
320 (($ <manifest-entry> entry-name entry-version entry-output)
321 (and (string=? entry-name name)
322 (or (not entry-output) (not output)
323 (string=? entry-output output))
324 (or (not version)
325 (string=? entry-version version))))))))
326
327(define (manifest-remove manifest patterns)
328 "Remove entries for each of PATTERNS from MANIFEST. Each item in PATTERNS
329must be a manifest-pattern."
330 (define (remove-entry pattern lst)
331 (remove (entry-predicate pattern) lst))
332
333 (make-manifest (fold remove-entry
cc4ecc2d 334 (manifest-entries manifest)
a2078770 335 patterns)))
cc4ecc2d 336
f7554030
AK
337(define (manifest-add manifest entries)
338 "Add a list of manifest ENTRIES to MANIFEST and return new manifest.
339Remove MANIFEST entries that have the same name and output as ENTRIES."
340 (define (same-entry? entry name output)
341 (match entry
342 (($ <manifest-entry> entry-name _ entry-output _ ...)
343 (and (equal? name entry-name)
344 (equal? output entry-output)))))
345
346 (make-manifest
347 (append entries
348 (fold (lambda (entry result)
349 (match entry
350 (($ <manifest-entry> name _ out _ ...)
351 (filter (negate (cut same-entry? <> name out))
352 result))))
353 (manifest-entries manifest)
354 entries))))
355
ef8993e2
LC
356(define (manifest-lookup manifest pattern)
357 "Return the first item of MANIFEST that matches PATTERN, or #f if there is
358no match.."
359 (find (entry-predicate pattern)
360 (manifest-entries manifest)))
361
a2078770
LC
362(define (manifest-installed? manifest pattern)
363 "Return #t if MANIFEST has an entry matching PATTERN (a manifest-pattern),
364#f otherwise."
ef8993e2 365 (->bool (manifest-lookup manifest pattern)))
cc4ecc2d 366
a2078770
LC
367(define (manifest-matching-entries manifest patterns)
368 "Return all the entries of MANIFEST that match one of the PATTERNS."
369 (define predicates
370 (map entry-predicate patterns))
371
372 (define (matches? entry)
373 (any (lambda (pred)
374 (pred entry))
375 predicates))
376
377 (filter matches? (manifest-entries manifest)))
378
cc4ecc2d 379\f
343745c8
AK
380;;;
381;;; Manifest transactions.
382;;;
383
384(define-record-type* <manifest-transaction> manifest-transaction
385 make-manifest-transaction
386 manifest-transaction?
387 (install manifest-transaction-install ; list of <manifest-entry>
388 (default '()))
389 (remove manifest-transaction-remove ; list of <manifest-pattern>
390 (default '())))
391
c8c25704
LC
392(define (manifest-transaction-install-entry entry transaction)
393 "Augment TRANSACTION's set of installed packages with ENTRY, a
394<manifest-entry>."
395 (manifest-transaction
396 (inherit transaction)
397 (install
398 (cons entry (manifest-transaction-install transaction)))))
399
400(define (manifest-transaction-remove-pattern pattern transaction)
401 "Add PATTERN to TRANSACTION's list of packages to remove."
402 (manifest-transaction
403 (inherit transaction)
404 (remove
405 (cons pattern (manifest-transaction-remove transaction)))))
406
407(define (manifest-transaction-null? transaction)
408 "Return true if TRANSACTION has no effect---i.e., it neither installs nor
409remove software."
410 (match transaction
411 (($ <manifest-transaction> () ()) #t)
412 (($ <manifest-transaction> _ _) #f)))
413
79601521 414(define (manifest-transaction-effects manifest transaction)
46b23e1a
LC
415 "Compute the effect of applying TRANSACTION to MANIFEST. Return 4 values:
416the list of packages that would be removed, installed, upgraded, or downgraded
417when applying TRANSACTION to MANIFEST. Upgrades are represented as pairs
418where the head is the entry being upgraded and the tail is the entry that will
419replace it."
79601521
LC
420 (define (manifest-entry->pattern entry)
421 (manifest-pattern
422 (name (manifest-entry-name entry))
423 (output (manifest-entry-output entry))))
424
46b23e1a
LC
425 (let loop ((input (manifest-transaction-install transaction))
426 (install '())
427 (upgrade '())
428 (downgrade '()))
79601521
LC
429 (match input
430 (()
431 (let ((remove (manifest-transaction-remove transaction)))
432 (values (manifest-matching-entries manifest remove)
46b23e1a 433 (reverse install) (reverse upgrade) (reverse downgrade))))
79601521
LC
434 ((entry rest ...)
435 ;; Check whether installing ENTRY corresponds to the installation of a
436 ;; new package or to an upgrade.
437
438 ;; XXX: When the exact same output directory is installed, we're not
439 ;; really upgrading anything. Add a check for that case.
440 (let* ((pattern (manifest-entry->pattern entry))
46b23e1a
LC
441 (previous (manifest-lookup manifest pattern))
442 (newer? (and previous
3bea13bb
LC
443 (version>=? (manifest-entry-version entry)
444 (manifest-entry-version previous)))))
79601521 445 (loop rest
ef8993e2 446 (if previous install (cons entry install))
46b23e1a 447 (if (and previous newer?)
ef8993e2 448 (alist-cons previous entry upgrade)
46b23e1a
LC
449 upgrade)
450 (if (and previous (not newer?))
451 (alist-cons previous entry downgrade)
452 downgrade)))))))
79601521 453
343745c8 454(define (manifest-perform-transaction manifest transaction)
c8c25704 455 "Perform TRANSACTION on MANIFEST and return the new manifest."
343745c8
AK
456 (let ((install (manifest-transaction-install transaction))
457 (remove (manifest-transaction-remove transaction)))
458 (manifest-add (manifest-remove manifest remove)
459 install)))
460
343745c8 461\f
cc4ecc2d
LC
462;;;
463;;; Profiles.
464;;;
465
79ee406d 466(define (manifest-inputs manifest)
b4a4bec0 467 "Return a list of <gexp-input> objects for MANIFEST."
79ee406d 468 (append-map (match-lambda
b4a4bec0
LC
469 (($ <manifest-entry> name version output thing deps)
470 ;; THING may be a package or a file name. In the latter case,
471 ;; assume it's already valid. Ditto for DEPS.
472 (cons (gexp-input thing output) deps)))
79ee406d
LC
473 (manifest-entries manifest)))
474
d72d7833
SB
475(define (manifest-lookup-package manifest name)
476 "Return as a monadic value the first package or store path referenced by
477MANIFEST that named NAME, or #f if not found."
478 ;; Return as a monadic value the package or store path referenced by the
479 ;; manifest ENTRY, or #f if not referenced.
480 (define (entry-lookup-package entry)
481 (define (find-among-inputs inputs)
482 (find (lambda (input)
483 (and (package? input)
484 (equal? name (package-name input))))
485 inputs))
486 (define (find-among-store-items items)
487 (find (lambda (item)
488 (equal? name (package-name->name+version
489 (store-path-package-name item))))
490 items))
491
492 ;; TODO: Factorize.
493 (define references*
494 (store-lift references))
495
496 (with-monad %store-monad
497 (match (manifest-entry-item entry)
498 ((? package? package)
963521a3
SB
499 (match (cons (list (package-name package) package)
500 (package-transitive-inputs package))
d72d7833
SB
501 (((labels inputs . _) ...)
502 (return (find-among-inputs inputs)))))
503 ((? string? item)
504 (mlet %store-monad ((refs (references* item)))
505 (return (find-among-store-items refs)))))))
506
507 (anym %store-monad
508 entry-lookup-package (manifest-entries manifest)))
509
79ee406d
LC
510(define (info-dir-file manifest)
511 "Return a derivation that builds the 'dir' file for all the entries of
512MANIFEST."
2f0556ae
LC
513 (define texinfo ;lazy reference
514 (module-ref (resolve-interface '(gnu packages texinfo)) 'texinfo))
515 (define gzip ;lazy reference
516 (module-ref (resolve-interface '(gnu packages compression)) 'gzip))
517
79ee406d 518 (define build
99b231de
LC
519 (with-imported-modules '((guix build utils))
520 #~(begin
521 (use-modules (guix build utils)
522 (srfi srfi-1) (srfi srfi-26)
523 (ice-9 ftw))
524
525 (define (info-file? file)
526 (or (string-suffix? ".info" file)
527 (string-suffix? ".info.gz" file)))
528
529 (define (info-files top)
530 (let ((infodir (string-append top "/share/info")))
531 (map (cut string-append infodir "/" <>)
532 (or (scandir infodir info-file?) '()))))
533
534 (define (install-info info)
535 (setenv "PATH" (string-append #+gzip "/bin")) ;for info.gz files
536 (zero?
537 (system* (string-append #+texinfo "/bin/install-info") "--silent"
538 info (string-append #$output "/share/info/dir"))))
539
540 (mkdir-p (string-append #$output "/share/info"))
541 (exit (every install-info
542 (append-map info-files
543 '#$(manifest-inputs manifest)))))))
79ee406d 544
aa46a028 545 (gexp->derivation "info-dir" build
a7a4fd9a
LC
546 #:local-build? #t
547 #:substitutable? #f))
79ee406d 548
042bc828
FB
549(define (ghc-package-cache-file manifest)
550 "Return a derivation that builds the GHC 'package.cache' file for all the
aa46a028 551entries of MANIFEST, or #f if MANIFEST does not have any GHC packages."
99b231de 552 (define ghc ;lazy reference
042bc828
FB
553 (module-ref (resolve-interface '(gnu packages haskell)) 'ghc))
554
555 (define build
99b231de
LC
556 (with-imported-modules '((guix build utils))
557 #~(begin
558 (use-modules (guix build utils)
559 (srfi srfi-1) (srfi srfi-26)
560 (ice-9 ftw))
561
562 (define ghc-name-version
563 (let* ((base (basename #+ghc)))
564 (string-drop base
565 (+ 1 (string-index base #\-)))))
566
567 (define db-subdir
568 (string-append "lib/" ghc-name-version "/package.conf.d"))
569
570 (define db-dir
571 (string-append #$output "/" db-subdir))
572
573 (define (conf-files top)
574 (let ((db (string-append top "/" db-subdir)))
575 (if (file-exists? db)
576 (find-files db "\\.conf$")
577 '())))
578
579 (define (copy-conf-file conf)
580 (let ((base (basename conf)))
581 (copy-file conf (string-append db-dir "/" base))))
582
583 (system* (string-append #+ghc "/bin/ghc-pkg") "init" db-dir)
584 (for-each copy-conf-file
585 (append-map conf-files
586 (delete-duplicates
587 '#$(manifest-inputs manifest))))
588 (let ((success
589 (zero?
590 (system* (string-append #+ghc "/bin/ghc-pkg") "recache"
591 (string-append "--package-db=" db-dir)))))
592 (for-each delete-file (find-files db-dir "\\.conf$"))
593 (exit success)))))
042bc828 594
07eaecfa
LC
595 (with-monad %store-monad
596 ;; Don't depend on GHC when there's nothing to do.
597 (if (any (cut string-prefix? "ghc" <>)
598 (map manifest-entry-name (manifest-entries manifest)))
599 (gexp->derivation "ghc-package-cache" build
a7a4fd9a
LC
600 #:local-build? #t
601 #:substitutable? #f)
07eaecfa 602 (return #f))))
042bc828 603
536c3ee4
MW
604(define (ca-certificate-bundle manifest)
605 "Return a derivation that builds a single-file bundle containing the CA
606certificates in the /etc/ssl/certs sub-directories of the packages in
607MANIFEST. Single-file bundles are required by programs such as Git and Lynx."
608 ;; See <http://lists.gnu.org/archive/html/guix-devel/2015-02/msg00429.html>
609 ;; for a discussion.
610
611 (define glibc-utf8-locales ;lazy reference
612 (module-ref (resolve-interface '(gnu packages base)) 'glibc-utf8-locales))
613
614 (define build
99b231de
LC
615 (with-imported-modules '((guix build utils))
616 #~(begin
617 (use-modules (guix build utils)
618 (rnrs io ports)
619 (srfi srfi-1)
620 (srfi srfi-26)
621 (ice-9 ftw)
622 (ice-9 match))
623
624 (define (pem-file? file)
625 (string-suffix? ".pem" file))
626
627 (define (ca-files top)
628 (let ((cert-dir (string-append top "/etc/ssl/certs")))
629 (map (cut string-append cert-dir "/" <>)
630 (or (scandir cert-dir pem-file?) '()))))
631
632 (define (concatenate-files files result)
633 "Make RESULT the concatenation of all of FILES."
634 (define (dump file port)
635 (display (call-with-input-file file get-string-all)
636 port)
637 (newline port)) ;required, see <https://bugs.debian.org/635570>
638
639 (call-with-output-file result
640 (lambda (port)
641 (for-each (cut dump <> port) files))))
642
643 ;; Some file names in the NSS certificates are UTF-8 encoded so
644 ;; install a UTF-8 locale.
645 (setenv "LOCPATH"
646 (string-append #+glibc-utf8-locales "/lib/locale/"
647 #+(package-version glibc-utf8-locales)))
648 (setlocale LC_ALL "en_US.utf8")
649
650 (match (append-map ca-files '#$(manifest-inputs manifest))
651 (()
652 ;; Since there are no CA files, just create an empty directory. Do
653 ;; not create the etc/ssl/certs sub-directory, since that would
654 ;; wrongfully lead to a message about 'SSL_CERT_DIR' needing to be
655 ;; defined.
656 (mkdir #$output)
657 #t)
658 ((ca-files ...)
659 (let ((result (string-append #$output "/etc/ssl/certs")))
660 (mkdir-p result)
661 (concatenate-files ca-files
662 (string-append result
663 "/ca-certificates.crt"))
664 #t))))))
536c3ee4 665
aa46a028 666 (gexp->derivation "ca-certificate-bundle" build
a7a4fd9a
LC
667 #:local-build? #t
668 #:substitutable? #f))
aa46a028 669
b04af0ec
SB
670(define (gtk-icon-themes manifest)
671 "Return a derivation that unions all icon themes from manifest entries and
672creates the GTK+ 'icon-theme.cache' file for each theme."
d72d7833 673 (mlet %store-monad ((gtk+ (manifest-lookup-package manifest "gtk+")))
b04af0ec 674 (define build
99b231de
LC
675 (with-imported-modules '((guix build utils)
676 (guix build union)
677 (guix build profiles)
678 (guix search-paths)
679 (guix records))
680 #~(begin
681 (use-modules (guix build utils)
682 (guix build union)
683 (guix build profiles)
684 (srfi srfi-26)
685 (ice-9 ftw))
686
687 (let* ((destdir (string-append #$output "/share/icons"))
688 (icondirs (filter file-exists?
689 (map (cut string-append <> "/share/icons")
690 '#$(manifest-inputs manifest))))
691 (update-icon-cache (string-append
692 #+gtk+ "/bin/gtk-update-icon-cache")))
693
694 ;; Union all the icons.
695 (mkdir-p (string-append #$output "/share"))
696 (union-build destdir icondirs
697 #:log-port (%make-void-port "w"))
698
699 ;; Update the 'icon-theme.cache' file for each icon theme.
700 (for-each
701 (lambda (theme)
702 (let ((dir (string-append destdir "/" theme)))
703 ;; Occasionally DESTDIR contains plain files, such as
704 ;; "abiword_48.png". Ignore these.
705 (when (file-is-directory? dir)
706 (ensure-writable-directory dir)
707 (system* update-icon-cache "-t" dir "--quiet"))))
708 (scandir destdir (negate (cut member <> '("." "..")))))))))
b04af0ec
SB
709
710 ;; Don't run the hook when there's nothing to do.
711 (if gtk+
712 (gexp->derivation "gtk-icon-themes" build
a7a4fd9a
LC
713 #:local-build? #t
714 #:substitutable? #f)
b04af0ec
SB
715 (return #f))))
716
842cb820
SB
717(define (xdg-desktop-database manifest)
718 "Return a derivation that builds the @file{mimeinfo.cache} database from
719desktop files. It's used to query what applications can handle a given
720MIME type."
d72d7833
SB
721 (mlet %store-monad ((desktop-file-utils
722 (manifest-lookup-package
723 manifest "desktop-file-utils")))
724 (define build
99b231de
LC
725 (with-imported-modules '((guix build utils)
726 (guix build union))
727 #~(begin
728 (use-modules (srfi srfi-26)
729 (guix build utils)
730 (guix build union))
731 (let* ((destdir (string-append #$output "/share/applications"))
732 (appdirs (filter file-exists?
733 (map (cut string-append <>
734 "/share/applications")
735 '#$(manifest-inputs manifest))))
736 (update-desktop-database (string-append
737 #+desktop-file-utils
738 "/bin/update-desktop-database")))
739 (mkdir-p (string-append #$output "/share"))
740 (union-build destdir appdirs
741 #:log-port (%make-void-port "w"))
742 (exit (zero? (system* update-desktop-database destdir)))))))
842cb820 743
d72d7833
SB
744 ;; Don't run the hook when 'desktop-file-utils' is not referenced.
745 (if desktop-file-utils
746 (gexp->derivation "xdg-desktop-database" build
d72d7833
SB
747 #:local-build? #t
748 #:substitutable? #f)
749 (return #f))))
842cb820 750
6c06b1fd
SB
751(define (xdg-mime-database manifest)
752 "Return a derivation that builds the @file{mime.cache} database from manifest
753entries. It's used to query the MIME type of a given file."
801d316b
SB
754 (define shared-mime-info ; lazy reference
755 (module-ref (resolve-interface '(gnu packages gnome)) 'shared-mime-info))
756
757 (mlet %store-monad ((glib
d72d7833 758 (manifest-lookup-package
801d316b 759 manifest "glib")))
d72d7833 760 (define build
99b231de
LC
761 (with-imported-modules '((guix build utils)
762 (guix build union))
763 #~(begin
764 (use-modules (srfi srfi-26)
765 (guix build utils)
766 (guix build union))
767 (let* ((datadir (string-append #$output "/share"))
768 (destdir (string-append datadir "/mime"))
769 (pkgdirs (filter file-exists?
770 (map (cut string-append <>
771 "/share/mime/packages")
801d316b
SB
772 (cons #+shared-mime-info
773 '#$(manifest-inputs manifest)))))
99b231de
LC
774 (update-mime-database (string-append
775 #+shared-mime-info
776 "/bin/update-mime-database")))
777 (mkdir-p destdir)
778 (union-build (string-append destdir "/packages") pkgdirs
779 #:log-port (%make-void-port "w"))
780 (setenv "XDG_DATA_HOME" datadir)
781 (exit (zero? (system* update-mime-database destdir)))))))
d72d7833 782
801d316b
SB
783 ;; Don't run the hook when there are no GLib based applications.
784 (if glib
d72d7833 785 (gexp->derivation "xdg-mime-database" build
d72d7833
SB
786 #:local-build? #t
787 #:substitutable? #f)
788 (return #f))))
6c06b1fd 789
9eb5a449
AK
790(define (fonts-dir-file manifest)
791 "Return a derivation that builds the @file{fonts.dir} and @file{fonts.scale}
792files for the truetype fonts of the @var{manifest} entries."
793 (define mkfontscale
794 (module-ref (resolve-interface '(gnu packages xorg)) 'mkfontscale))
795
796 (define mkfontdir
797 (module-ref (resolve-interface '(gnu packages xorg)) 'mkfontdir))
798
799 (define build
800 #~(begin
801 (use-modules (srfi srfi-26)
802 (guix build utils)
803 (guix build union))
804 (let ((ttf-dirs (filter file-exists?
805 (map (cut string-append <>
806 "/share/fonts/truetype")
807 '#$(manifest-inputs manifest)))))
808 (mkdir #$output)
809 (if (null? ttf-dirs)
810 (exit #t)
811 (let* ((fonts-dir (string-append #$output "/share/fonts"))
812 (ttf-dir (string-append fonts-dir "/truetype"))
813 (mkfontscale (string-append #+mkfontscale
814 "/bin/mkfontscale"))
815 (mkfontdir (string-append #+mkfontdir
816 "/bin/mkfontdir")))
817 (mkdir-p fonts-dir)
818 (union-build ttf-dir ttf-dirs
819 #:log-port (%make-void-port "w"))
820 (with-directory-excursion ttf-dir
821 (exit (and (zero? (system* mkfontscale))
822 (zero? (system* mkfontdir))))))))))
823
824 (gexp->derivation "fonts-dir" build
825 #:modules '((guix build utils)
826 (guix build union))
827 #:local-build? #t
828 #:substitutable? #f))
829
aa46a028
LC
830(define %default-profile-hooks
831 ;; This is the list of derivation-returning procedures that are called by
832 ;; default when making a non-empty profile.
833 (list info-dir-file
9eb5a449 834 fonts-dir-file
aa46a028 835 ghc-package-cache-file
b04af0ec 836 ca-certificate-bundle
842cb820 837 gtk-icon-themes
6c06b1fd
SB
838 xdg-desktop-database
839 xdg-mime-database))
536c3ee4
MW
840
841(define* (profile-derivation manifest
842 #:key
e5f04c2d
LC
843 (hooks %default-profile-hooks)
844 system)
79ee406d 845 "Return a derivation that builds a profile (aka. 'user environment') with
aa46a028
LC
846the given MANIFEST. The profile includes additional derivations returned by
847the monadic procedures listed in HOOKS--such as an Info 'dir' file, etc."
e5f04c2d
LC
848 (mlet %store-monad ((system (if system
849 (return system)
850 (current-system)))
851 (extras (if (null? (manifest-entries manifest))
aa46a028
LC
852 (return '())
853 (sequence %store-monad
07eaecfa
LC
854 (map (lambda (hook)
855 (hook manifest))
856 hooks)))))
79ee406d 857 (define inputs
eeae0b3c
SB
858 (append (filter-map (lambda (drv)
859 (and (derivation? drv)
860 (gexp-input drv)))
07eaecfa 861 extras)
536c3ee4 862 (manifest-inputs manifest)))
79ee406d
LC
863
864 (define builder
99b231de
LC
865 (with-imported-modules '((guix build profiles)
866 (guix build union)
867 (guix build utils)
868 (guix search-paths)
869 (guix records))
870 #~(begin
871 (use-modules (guix build profiles)
872 (guix search-paths)
873 (srfi srfi-1))
874
875 (setvbuf (current-output-port) _IOLBF)
876 (setvbuf (current-error-port) _IOLBF)
877
878 (define search-paths
879 ;; Search paths of MANIFEST's packages, converted back to their
880 ;; record form.
881 (map sexp->search-path-specification
882 (delete-duplicates
883 '#$(map search-path-specification->sexp
884 (append-map manifest-entry-search-paths
885 (manifest-entries manifest))))))
886
887 (build-profile #$output '#$inputs
888 #:manifest '#$(manifest->gexp manifest)
889 #:search-paths search-paths))))
79ee406d
LC
890
891 (gexp->derivation "profile" builder
40d71e44 892 #:system system
a7a4fd9a
LC
893
894 ;; Not worth offloading.
895 #:local-build? #t
896
897 ;; Disable substitution because it would trigger a
898 ;; connection to the substitute server, which is likely
899 ;; to have no substitute to offer.
900 #:substitutable? #f)))
cc4ecc2d
LC
901
902(define (profile-regexp profile)
903 "Return a regular expression that matches PROFILE's name and number."
904 (make-regexp (string-append "^" (regexp-quote (basename profile))
905 "-([0-9]+)")))
906
907(define (generation-number profile)
908 "Return PROFILE's number or 0. An absolute file name must be used."
909 (or (and=> (false-if-exception (regexp-exec (profile-regexp profile)
910 (basename (readlink profile))))
911 (compose string->number (cut match:substring <> 1)))
912 0))
913
914(define (generation-numbers profile)
915 "Return the sorted list of generation numbers of PROFILE, or '(0) if no
916former profiles were found."
917 (define* (scandir name #:optional (select? (const #t))
918 (entry<? (@ (ice-9 i18n) string-locale<?)))
919 ;; XXX: Bug-fix version introduced in Guile v2.0.6-62-g139ce19.
920 (define (enter? dir stat result)
921 (and stat (string=? dir name)))
922
923 (define (visit basename result)
924 (if (select? basename)
925 (cons basename result)
926 result))
927
928 (define (leaf name stat result)
929 (and result
930 (visit (basename name) result)))
931
932 (define (down name stat result)
933 (visit "." '()))
934
935 (define (up name stat result)
936 (visit ".." result))
937
938 (define (skip name stat result)
939 ;; All the sub-directories are skipped.
940 (visit (basename name) result))
941
942 (define (error name* stat errno result)
943 (if (string=? name name*) ; top-level NAME is unreadable
944 result
945 (visit (basename name*) result)))
946
947 (and=> (file-system-fold enter? leaf down up skip error #f name lstat)
948 (lambda (files)
949 (sort files entry<?))))
950
951 (match (scandir (dirname profile)
952 (cute regexp-exec (profile-regexp profile) <>))
953 (#f ; no profile directory
954 '(0))
955 (() ; no profiles
956 '(0))
957 ((profiles ...) ; former profiles around
958 (sort (map (compose string->number
959 (cut match:substring <> 1)
960 (cute regexp-exec (profile-regexp profile) <>))
961 profiles)
962 <))))
963
f452e8ff
AK
964(define (profile-generations profile)
965 "Return a list of PROFILE's generations."
966 (let ((generations (generation-numbers profile)))
967 (if (equal? generations '(0))
968 '()
969 generations)))
970
3ccde087
AK
971(define* (relative-generation profile shift #:optional
972 (current (generation-number profile)))
973 "Return PROFILE's generation shifted from the CURRENT generation by SHIFT.
974SHIFT is a positive or negative number.
975Return #f if there is no such generation."
976 (let* ((abs-shift (abs shift))
977 (numbers (profile-generations profile))
978 (from-current (memq current
979 (if (negative? shift)
980 (reverse numbers)
981 numbers))))
982 (and from-current
983 (< abs-shift (length from-current))
984 (list-ref from-current abs-shift))))
985
986(define* (previous-generation-number profile #:optional
987 (number (generation-number profile)))
cc4ecc2d
LC
988 "Return the number of the generation before generation NUMBER of
989PROFILE, or 0 if none exists. It could be NUMBER - 1, but it's not the
990case when generations have been deleted (there are \"holes\")."
3ccde087
AK
991 (or (relative-generation profile -1 number)
992 0))
cc4ecc2d
LC
993
994(define (generation-file-name profile generation)
995 "Return the file name for PROFILE's GENERATION."
996 (format #f "~a-~a-link" profile generation))
997
998(define (generation-time profile number)
999 "Return the creation time of a generation in the UTC format."
1000 (make-time time-utc 0
1001 (stat:ctime (stat (generation-file-name profile number)))))
1002
06d45f45
LC
1003(define (link-to-empty-profile store generation)
1004 "Link GENERATION, a string, to the empty profile. An error is raised if
1005that fails."
1006 (let* ((drv (run-with-store store
1007 (profile-derivation (manifest '()))))
1008 (prof (derivation->output-path drv "out")))
1009 (build-derivations store (list drv))
1010 (switch-symlinks generation prof)))
1011
1012(define (switch-to-generation profile number)
1013 "Atomically switch PROFILE to the generation NUMBER. Return the number of
1014the generation that was current before switching."
1015 (let ((current (generation-number profile))
1016 (generation (generation-file-name profile number)))
1017 (cond ((not (file-exists? profile))
1018 (raise (condition (&profile-not-found-error
1019 (profile profile)))))
1020 ((not (file-exists? generation))
1021 (raise (condition (&missing-generation-error
1022 (profile profile)
1023 (generation number)))))
1024 (else
1025 (switch-symlinks profile generation)
1026 current))))
1027
1028(define (switch-to-previous-generation profile)
1029 "Atomically switch PROFILE to the previous generation. Return the former
1030generation number and the current one."
1031 (let ((previous (previous-generation-number profile)))
1032 (values (switch-to-generation profile previous)
1033 previous)))
1034
1035(define (roll-back store profile)
1036 "Roll back to the previous generation of PROFILE. Return the number of the
1037generation that was current before switching and the new generation number."
1038 (let* ((number (generation-number profile))
1039 (previous-number (previous-generation-number profile number))
1040 (previous-generation (generation-file-name profile previous-number)))
1041 (cond ((not (file-exists? profile)) ;invalid profile
1042 (raise (condition (&profile-not-found-error
1043 (profile profile)))))
1044 ((zero? number) ;empty profile
1045 (values number number))
1046 ((or (zero? previous-number) ;going to emptiness
1047 (not (file-exists? previous-generation)))
1048 (link-to-empty-profile store previous-generation)
1049 (switch-to-previous-generation profile))
1050 (else ;anything else
1051 (switch-to-previous-generation profile)))))
1052
1053(define (delete-generation store profile number)
1054 "Delete generation with NUMBER from PROFILE. Return the file name of the
1055generation that has been deleted, or #f if nothing was done (for instance
1056because the NUMBER is zero.)"
1057 (define (delete-and-return)
1058 (let ((generation (generation-file-name profile number)))
1059 (delete-file generation)
1060 generation))
1061
1062 (let* ((current-number (generation-number profile))
1063 (previous-number (previous-generation-number profile number))
1064 (previous-generation (generation-file-name profile previous-number)))
1065 (cond ((zero? number) #f) ;do not delete generation 0
1066 ((and (= number current-number)
1067 (not (file-exists? previous-generation)))
1068 (link-to-empty-profile store previous-generation)
1069 (switch-to-previous-generation profile)
1070 (delete-and-return))
1071 ((= number current-number)
1072 (roll-back store profile)
1073 (delete-and-return))
1074 (else
1075 (delete-and-return)))))
1076
cc4ecc2d 1077;;; profiles.scm ends here