services: zabbix-front-end: Improve hint and error messages.
[jackhill/guix/guix.git] / guix / profiles.scm
CommitLineData
cc4ecc2d 1;;; GNU Guix --- Functional package management for GNU
435603a1 2;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2018 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>
7ddc1780 7;;; Copyright © 2016 Ricardo Wurmus <rekado@elephly.net>
9008debc 8;;; Copyright © 2016 Chris Marusich <cmmarusich@gmail.com>
0a5ce0d1 9;;; Copyright © 2017 Huang Ying <huang.ying.caritas@gmail.com>
a0b87ef8 10;;; Copyright © 2017 Maxim Cournoyer <maxim.cournoyer@gmail.com>
cc4ecc2d
LC
11;;;
12;;; This file is part of GNU Guix.
13;;;
14;;; GNU Guix is free software; you can redistribute it and/or modify it
15;;; under the terms of the GNU General Public License as published by
16;;; the Free Software Foundation; either version 3 of the License, or (at
17;;; your option) any later version.
18;;;
19;;; GNU Guix is distributed in the hope that it will be useful, but
20;;; WITHOUT ANY WARRANTY; without even the implied warranty of
21;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22;;; GNU General Public License for more details.
23;;;
24;;; You should have received a copy of the GNU General Public License
25;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
26
27(define-module (guix profiles)
efcb4441 28 #:use-module ((guix config) #:select (%state-directory))
97425486
LC
29 #:use-module ((guix utils) #:hide (package-name->name+version))
30 #:use-module ((guix build utils)
77dcfb4c
LC
31 #:select (package-name->name+version mkdir-p))
32 #:use-module (guix i18n)
cc4ecc2d 33 #:use-module (guix records)
cc4ecc2d 34 #:use-module (guix packages)
e89431bf
LC
35 #:use-module (guix derivations)
36 #:use-module (guix search-paths)
a54c94a4 37 #:use-module (guix gexp)
b8396f96 38 #:use-module (guix modules)
79ee406d 39 #:use-module (guix monads)
e87f0591 40 #:use-module (guix store)
a654dc4b
LC
41 #:use-module (guix sets)
42 #:use-module (ice-9 vlist)
cc4ecc2d
LC
43 #:use-module (ice-9 match)
44 #:use-module (ice-9 regex)
45 #:use-module (ice-9 ftw)
343745c8 46 #:use-module (ice-9 format)
cc4ecc2d
LC
47 #:use-module (srfi srfi-1)
48 #:use-module (srfi srfi-9)
79601521 49 #:use-module (srfi srfi-11)
cc4ecc2d
LC
50 #:use-module (srfi srfi-19)
51 #:use-module (srfi srfi-26)
c0c018f1
AK
52 #:use-module (srfi srfi-34)
53 #:use-module (srfi srfi-35)
54 #:export (&profile-error
55 profile-error?
56 profile-error-profile
57 &profile-not-found-error
58 profile-not-found-error?
fdc4f665 59 &profile-collision-error
a654dc4b
LC
60 profile-collision-error?
61 profile-collision-error-entry
62 profile-collision-error-conflict
c0c018f1
AK
63 &missing-generation-error
64 missing-generation-error?
65 missing-generation-error-generation
66
67 manifest make-manifest
cc4ecc2d
LC
68 manifest?
69 manifest-entries
a654dc4b 70 manifest-transitive-entries
cc4ecc2d
LC
71
72 <manifest-entry> ; FIXME: eventually make it internal
73 manifest-entry
74 manifest-entry?
75 manifest-entry-name
76 manifest-entry-version
77 manifest-entry-output
a54c94a4 78 manifest-entry-item
cc4ecc2d 79 manifest-entry-dependencies
dedb17ad 80 manifest-entry-search-paths
b3a00885 81 manifest-entry-parent
f6f2346f 82 manifest-entry-properties
cc4ecc2d 83
a2078770
LC
84 manifest-pattern
85 manifest-pattern?
03763d64
LC
86 manifest-pattern-name
87 manifest-pattern-version
88 manifest-pattern-output
a2078770 89
cc4ecc2d 90 manifest-remove
f7554030 91 manifest-add
ef8993e2 92 manifest-lookup
cc4ecc2d 93 manifest-installed?
a2078770 94 manifest-matching-entries
f03df3ee 95 manifest-search-paths
cc4ecc2d 96
343745c8
AK
97 manifest-transaction
98 manifest-transaction?
99 manifest-transaction-install
100 manifest-transaction-remove
c8c25704
LC
101 manifest-transaction-install-entry
102 manifest-transaction-remove-pattern
103 manifest-transaction-null?
6d382339 104 manifest-transaction-removal-candidate?
343745c8 105 manifest-perform-transaction
79601521 106 manifest-transaction-effects
343745c8 107
cc4ecc2d 108 profile-manifest
462f5cca 109 package->manifest-entry
8404ed5c 110 packages->manifest
849a1b81 111 ca-certificate-bundle
aa46a028 112 %default-profile-hooks
cc4ecc2d 113 profile-derivation
78d55b70 114 profile-search-paths
06d45f45 115
cc4ecc2d
LC
116 generation-number
117 generation-numbers
f452e8ff 118 profile-generations
9008debc 119 relative-generation-spec->number
3ccde087 120 relative-generation
cc4ecc2d
LC
121 previous-generation-number
122 generation-time
06d45f45
LC
123 generation-file-name
124 switch-to-generation
125 roll-back
efcb4441
LC
126 delete-generation
127
128 %user-profile-directory
129 %profile-directory
130 %current-profile
77dcfb4c 131 ensure-profile-directory
efcb4441
LC
132 canonicalize-profile
133 user-friendly-profile))
cc4ecc2d
LC
134
135;;; Commentary:
136;;;
137;;; Tools to create and manipulate profiles---i.e., the representation of a
138;;; set of installed packages.
139;;;
140;;; Code:
141
142\f
c0c018f1
AK
143;;;
144;;; Condition types.
145;;;
146
147(define-condition-type &profile-error &error
148 profile-error?
149 (profile profile-error-profile))
150
151(define-condition-type &profile-not-found-error &profile-error
152 profile-not-found-error?)
153
a654dc4b
LC
154(define-condition-type &profile-collision-error &error
155 profile-collision-error?
156 (entry profile-collision-error-entry) ;<manifest-entry>
157 (conflict profile-collision-error-conflict)) ;<manifest-entry>
158
c0c018f1
AK
159(define-condition-type &missing-generation-error &profile-error
160 missing-generation-error?
161 (generation missing-generation-error-generation))
162
163\f
cc4ecc2d
LC
164;;;
165;;; Manifests.
166;;;
167
168(define-record-type <manifest>
169 (manifest entries)
170 manifest?
171 (entries manifest-entries)) ; list of <manifest-entry>
172
173;; Convenient alias, to avoid name clashes.
174(define make-manifest manifest)
175
176(define-record-type* <manifest-entry> manifest-entry
177 make-manifest-entry
178 manifest-entry?
179 (name manifest-entry-name) ; string
180 (version manifest-entry-version) ; string
181 (output manifest-entry-output ; string
182 (default "out"))
3636b1c7 183 (item manifest-entry-item) ; package | file-like | store path
55b4715f 184 (dependencies manifest-entry-dependencies ; <manifest-entry>*
dedb17ad
LC
185 (default '()))
186 (search-paths manifest-entry-search-paths ; search-path-specification*
b3a00885
LC
187 (default '()))
188 (parent manifest-entry-parent ; promise (#f | <manifest-entry>)
f6f2346f
LC
189 (default (delay #f)))
190 (properties manifest-entry-properties ; list of symbol/value pairs
191 (default '())))
cc4ecc2d 192
a2078770
LC
193(define-record-type* <manifest-pattern> manifest-pattern
194 make-manifest-pattern
195 manifest-pattern?
196 (name manifest-pattern-name) ; string
197 (version manifest-pattern-version ; string | #f
198 (default #f))
199 (output manifest-pattern-output ; string | #f
200 (default "out")))
201
2e2b5ad7
LC
202(define (manifest-transitive-entries manifest)
203 "Return the entries of MANIFEST along with their propagated inputs,
204recursively."
205 (let loop ((entries (manifest-entries manifest))
206 (result '())
207 (visited (set))) ;compare with 'equal?'
208 (match entries
209 (()
210 (reverse result))
211 ((head . tail)
212 (if (set-contains? visited head)
213 (loop tail result visited)
214 (loop (append (manifest-entry-dependencies head)
215 tail)
216 (cons head result)
217 (set-insert head visited)))))))
218
cc4ecc2d
LC
219(define (profile-manifest profile)
220 "Return the PROFILE's manifest."
221 (let ((file (string-append profile "/manifest")))
222 (if (file-exists? file)
223 (call-with-input-file file read-manifest)
224 (manifest '()))))
225
a654dc4b
LC
226(define (manifest-entry-lookup manifest)
227 "Return a lookup procedure for the entries of MANIFEST. The lookup
228procedure takes two arguments: the entry name and output."
229 (define mapping
230 (let loop ((entries (manifest-entries manifest))
231 (mapping vlist-null))
232 (fold (lambda (entry result)
233 (vhash-cons (cons (manifest-entry-name entry)
234 (manifest-entry-output entry))
235 entry
236 (loop (manifest-entry-dependencies entry)
237 result)))
238 mapping
239 entries)))
240
241 (lambda (name output)
242 (match (vhash-assoc (cons name output) mapping)
243 ((_ . entry) entry)
244 (#f #f))))
245
246(define* (lower-manifest-entry entry system #:key target)
247 "Lower ENTRY for SYSTEM and TARGET such that its 'item' field is a store
248file name."
249 (let ((item (manifest-entry-item entry)))
250 (if (string? item)
251 (with-monad %store-monad
252 (return entry))
253 (mlet %store-monad ((drv (lower-object item system
254 #:target target))
255 (output -> (manifest-entry-output entry)))
256 (return (manifest-entry
257 (inherit entry)
258 (item (derivation->output-path drv output))))))))
259
260(define* (check-for-collisions manifest system #:key target)
261 "Check whether the entries of MANIFEST conflict with one another; raise a
262'&profile-collision-error' when a conflict is encountered."
263 (define lookup
264 (manifest-entry-lookup manifest))
265
266 (with-monad %store-monad
267 (foldm %store-monad
268 (lambda (entry result)
269 (match (lookup (manifest-entry-name entry)
270 (manifest-entry-output entry))
271 ((? manifest-entry? second) ;potential conflict
272 (mlet %store-monad ((first (lower-manifest-entry entry system
273 #:target
274 target))
275 (second (lower-manifest-entry second system
276 #:target
277 target)))
278 (if (string=? (manifest-entry-item first)
279 (manifest-entry-item second))
280 (return result)
281 (raise (condition
282 (&profile-collision-error
283 (entry first)
284 (conflict second)))))))
285 (#f ;no conflict
286 (return result))))
287 #t
288 (manifest-transitive-entries manifest))))
289
b3a00885 290(define* (package->manifest-entry package #:optional (output "out")
2b73d828
LC
291 #:key (parent (delay #f))
292 (properties '()))
9e90fc77 293 "Return a manifest entry for the OUTPUT of package PACKAGE."
b3a00885
LC
294 ;; For each dependency, keep a promise pointing to its "parent" entry.
295 (letrec* ((deps (map (match-lambda
296 ((label package)
297 (package->manifest-entry package
298 #:parent (delay entry)))
299 ((label package output)
300 (package->manifest-entry package output
301 #:parent (delay entry))))
302 (package-propagated-inputs package)))
303 (entry (manifest-entry
304 (name (package-name package))
305 (version (package-version package))
306 (output output)
307 (item package)
308 (dependencies (delete-duplicates deps))
309 (search-paths
310 (package-transitive-native-search-paths package))
2b73d828
LC
311 (parent parent)
312 (properties properties))))
b3a00885 313 entry))
462f5cca 314
8404ed5c
DT
315(define (packages->manifest packages)
316 "Return a list of manifest entries, one for each item listed in PACKAGES.
317Elements of PACKAGES can be either package objects or package/string tuples
318denoting a specific output of a package."
811b21fb
LC
319 (define inferiors-loaded?
320 ;; This hack allows us to provide seamless integration for inferior
321 ;; packages while not having a hard dependency on (guix inferior).
322 (resolve-module '(guix inferior) #f #f #:ensure #f))
323
324 (define (inferior->entry)
325 (module-ref (resolve-interface '(guix inferior))
326 'inferior-package->manifest-entry))
327
8404ed5c
DT
328 (manifest
329 (map (match-lambda
811b21fb
LC
330 ((package output)
331 (package->manifest-entry package output))
332 ((? package? package)
333 (package->manifest-entry package))
334 ((thing output)
335 (if inferiors-loaded?
336 ((inferior->entry) thing output)
337 (throw 'wrong-type-arg 'packages->manifest
338 "Wrong package object: ~S" (list thing) (list thing))))
339 (thing
340 (if inferiors-loaded?
341 ((inferior->entry) thing)
342 (throw 'wrong-type-arg 'packages->manifest
343 "Wrong package object: ~S" (list thing) (list thing)))))
8404ed5c
DT
344 packages)))
345
a54c94a4
LC
346(define (manifest->gexp manifest)
347 "Return a representation of MANIFEST as a gexp."
348 (define (entry->gexp entry)
cc4ecc2d 349 (match entry
dedb17ad 350 (($ <manifest-entry> name version output (? string? path)
f6f2346f 351 (deps ...) (search-paths ...) _ (properties ...))
dedb17ad 352 #~(#$name #$version #$output #$path
55b4715f 353 (propagated-inputs #$(map entry->gexp deps))
dedb17ad 354 (search-paths #$(map search-path-specification->sexp
f6f2346f
LC
355 search-paths))
356 (properties . #$properties)))
3636b1c7 357 (($ <manifest-entry> name version output package
f6f2346f 358 (deps ...) (search-paths ...) _ (properties ...))
a54c94a4 359 #~(#$name #$version #$output
dedb17ad 360 (ungexp package (or output "out"))
55b4715f 361 (propagated-inputs #$(map entry->gexp deps))
dedb17ad 362 (search-paths #$(map search-path-specification->sexp
f6f2346f
LC
363 search-paths))
364 (properties . #$properties)))))
cc4ecc2d
LC
365
366 (match manifest
367 (($ <manifest> (entries ...))
55b4715f 368 #~(manifest (version 3)
a54c94a4 369 (packages #$(map entry->gexp entries))))))
cc4ecc2d 370
dedb17ad
LC
371(define (find-package name version)
372 "Return a package from the distro matching NAME and possibly VERSION. This
373procedure is here for backward-compatibility and will eventually vanish."
374 (define find-best-packages-by-name ;break abstractions
375 (module-ref (resolve-interface '(gnu packages))
376 'find-best-packages-by-name))
377
378 ;; Use 'find-best-packages-by-name' and not 'find-packages-by-name'; the
379 ;; former traverses the module tree only once and then allows for efficient
380 ;; access via a vhash.
381 (match (find-best-packages-by-name name version)
382 ((p _ ...) p)
383 (_
384 (match (find-best-packages-by-name name #f)
385 ((p _ ...) p)
386 (_ #f)))))
387
cc4ecc2d
LC
388(define (sexp->manifest sexp)
389 "Parse SEXP as a manifest."
dedb17ad
LC
390 (define (infer-search-paths name version)
391 ;; Infer the search path specifications for NAME-VERSION by looking up a
392 ;; same-named package in the distro. Useful for the old manifest formats
393 ;; that did not store search path info.
394 (let ((package (find-package name version)))
395 (if package
396 (package-native-search-paths package)
397 '())))
398
b3a00885 399 (define (infer-dependency item parent)
55b4715f
LC
400 ;; Return a <manifest-entry> for ITEM.
401 (let-values (((name version)
402 (package-name->name+version
403 (store-path-package-name item))))
404 (manifest-entry
405 (name name)
406 (version version)
b3a00885
LC
407 (item item)
408 (parent parent))))
409
410 (define* (sexp->manifest-entry sexp #:optional (parent (delay #f)))
411 (match sexp
412 ((name version output path
413 ('propagated-inputs deps)
414 ('search-paths search-paths)
415 extra-stuff ...)
416 ;; For each of DEPS, keep a promise pointing to ENTRY.
417 (letrec* ((deps* (map (cut sexp->manifest-entry <> (delay entry))
418 deps))
419 (entry (manifest-entry
420 (name name)
421 (version version)
422 (output output)
423 (item path)
424 (dependencies deps*)
425 (search-paths (map sexp->search-path-specification
426 search-paths))
f6f2346f
LC
427 (parent parent)
428 (properties (or (assoc-ref extra-stuff 'properties)
429 '())))))
b3a00885 430 entry))))
55b4715f 431
cc4ecc2d
LC
432 (match sexp
433 (('manifest ('version 0)
434 ('packages ((name version output path) ...)))
435 (manifest
436 (map (lambda (name version output path)
437 (manifest-entry
55b4715f
LC
438 (name name)
439 (version version)
440 (output output)
441 (item path)
442 (search-paths (infer-search-paths name version))))
cc4ecc2d
LC
443 name version output path)))
444
445 ;; Version 1 adds a list of propagated inputs to the
446 ;; name/version/output/path tuples.
447 (('manifest ('version 1)
448 ('packages ((name version output path deps) ...)))
449 (manifest
450 (map (lambda (name version output path deps)
d34736c5
LC
451 ;; Up to Guix 0.7 included, dependencies were listed as ("gmp"
452 ;; "/gnu/store/...-gmp") for instance. Discard the 'label' in
453 ;; such lists.
454 (let ((deps (match deps
455 (((labels directories) ...)
456 directories)
457 ((directories ...)
458 directories))))
b3a00885
LC
459 (letrec* ((deps* (map (cute infer-dependency <> (delay entry))
460 deps))
461 (entry (manifest-entry
462 (name name)
463 (version version)
464 (output output)
465 (item path)
466 (dependencies deps*)
467 (search-paths
468 (infer-search-paths name version)))))
469 entry)))
cc4ecc2d
LC
470 name version output path deps)))
471
dedb17ad
LC
472 ;; Version 2 adds search paths and is slightly more verbose.
473 (('manifest ('version 2 minor-version ...)
474 ('packages ((name version output path
475 ('propagated-inputs deps)
476 ('search-paths search-paths)
477 extra-stuff ...)
478 ...)))
479 (manifest
480 (map (lambda (name version output path deps search-paths)
b3a00885
LC
481 (letrec* ((deps* (map (cute infer-dependency <> (delay entry))
482 deps))
483 (entry (manifest-entry
484 (name name)
485 (version version)
486 (output output)
487 (item path)
488 (dependencies deps*)
489 (search-paths
490 (map sexp->search-path-specification
491 search-paths)))))
492 entry))
dedb17ad 493 name version output path deps search-paths)))
55b4715f
LC
494
495 ;; Version 3 represents DEPS as full-blown manifest entries.
496 (('manifest ('version 3 minor-version ...)
497 ('packages (entries ...)))
b3a00885 498 (manifest (map sexp->manifest-entry entries)))
cc4ecc2d 499 (_
88aab8e3
LC
500 (raise (condition
501 (&message (message "unsupported manifest format")))))))
cc4ecc2d
LC
502
503(define (read-manifest port)
504 "Return the packages listed in MANIFEST."
505 (sexp->manifest (read port)))
506
a2078770
LC
507(define (entry-predicate pattern)
508 "Return a procedure that returns #t when passed a manifest entry that
509matches NAME/OUTPUT/VERSION. OUTPUT and VERSION may be #f, in which case they
510are ignored."
511 (match pattern
512 (($ <manifest-pattern> name version output)
513 (match-lambda
514 (($ <manifest-entry> entry-name entry-version entry-output)
515 (and (string=? entry-name name)
516 (or (not entry-output) (not output)
517 (string=? entry-output output))
518 (or (not version)
519 (string=? entry-version version))))))))
520
521(define (manifest-remove manifest patterns)
522 "Remove entries for each of PATTERNS from MANIFEST. Each item in PATTERNS
523must be a manifest-pattern."
524 (define (remove-entry pattern lst)
525 (remove (entry-predicate pattern) lst))
526
527 (make-manifest (fold remove-entry
cc4ecc2d 528 (manifest-entries manifest)
a2078770 529 patterns)))
cc4ecc2d 530
f7554030
AK
531(define (manifest-add manifest entries)
532 "Add a list of manifest ENTRIES to MANIFEST and return new manifest.
533Remove MANIFEST entries that have the same name and output as ENTRIES."
534 (define (same-entry? entry name output)
535 (match entry
435603a1 536 (($ <manifest-entry> entry-name _ entry-output _)
f7554030
AK
537 (and (equal? name entry-name)
538 (equal? output entry-output)))))
539
540 (make-manifest
435603a1
LC
541 (fold (lambda (entry result) ;XXX: quadratic
542 (match entry
543 (($ <manifest-entry> name _ out _)
544 (cons entry
545 (remove (cut same-entry? <> name out)
546 result)))))
547 (manifest-entries manifest)
548 entries)))
f7554030 549
ef8993e2
LC
550(define (manifest-lookup manifest pattern)
551 "Return the first item of MANIFEST that matches PATTERN, or #f if there is
552no match.."
553 (find (entry-predicate pattern)
554 (manifest-entries manifest)))
555
a2078770
LC
556(define (manifest-installed? manifest pattern)
557 "Return #t if MANIFEST has an entry matching PATTERN (a manifest-pattern),
558#f otherwise."
ef8993e2 559 (->bool (manifest-lookup manifest pattern)))
cc4ecc2d 560
a2078770
LC
561(define (manifest-matching-entries manifest patterns)
562 "Return all the entries of MANIFEST that match one of the PATTERNS."
563 (define predicates
564 (map entry-predicate patterns))
565
566 (define (matches? entry)
567 (any (lambda (pred)
568 (pred entry))
569 predicates))
570
571 (filter matches? (manifest-entries manifest)))
572
f03df3ee
LC
573(define (manifest-search-paths manifest)
574 "Return the list of search path specifications that apply to MANIFEST,
575including the search path specification for $PATH."
576 (delete-duplicates
577 (cons $PATH
578 (append-map manifest-entry-search-paths
579 (manifest-entries manifest)))))
580
cc4ecc2d 581\f
343745c8
AK
582;;;
583;;; Manifest transactions.
584;;;
585
586(define-record-type* <manifest-transaction> manifest-transaction
587 make-manifest-transaction
588 manifest-transaction?
589 (install manifest-transaction-install ; list of <manifest-entry>
590 (default '()))
591 (remove manifest-transaction-remove ; list of <manifest-pattern>
592 (default '())))
593
c8c25704
LC
594(define (manifest-transaction-install-entry entry transaction)
595 "Augment TRANSACTION's set of installed packages with ENTRY, a
596<manifest-entry>."
597 (manifest-transaction
598 (inherit transaction)
599 (install
600 (cons entry (manifest-transaction-install transaction)))))
601
602(define (manifest-transaction-remove-pattern pattern transaction)
603 "Add PATTERN to TRANSACTION's list of packages to remove."
604 (manifest-transaction
605 (inherit transaction)
606 (remove
607 (cons pattern (manifest-transaction-remove transaction)))))
608
609(define (manifest-transaction-null? transaction)
610 "Return true if TRANSACTION has no effect---i.e., it neither installs nor
611remove software."
612 (match transaction
613 (($ <manifest-transaction> () ()) #t)
614 (($ <manifest-transaction> _ _) #f)))
615
6d382339
LC
616(define (manifest-transaction-removal-candidate? entry transaction)
617 "Return true if ENTRY is a candidate for removal in TRANSACTION."
618 (any (lambda (pattern)
619 ((entry-predicate pattern) entry))
620 (manifest-transaction-remove transaction)))
621
79601521 622(define (manifest-transaction-effects manifest transaction)
46b23e1a
LC
623 "Compute the effect of applying TRANSACTION to MANIFEST. Return 4 values:
624the list of packages that would be removed, installed, upgraded, or downgraded
625when applying TRANSACTION to MANIFEST. Upgrades are represented as pairs
626where the head is the entry being upgraded and the tail is the entry that will
627replace it."
79601521
LC
628 (define (manifest-entry->pattern entry)
629 (manifest-pattern
630 (name (manifest-entry-name entry))
631 (output (manifest-entry-output entry))))
632
46b23e1a
LC
633 (let loop ((input (manifest-transaction-install transaction))
634 (install '())
635 (upgrade '())
636 (downgrade '()))
79601521
LC
637 (match input
638 (()
639 (let ((remove (manifest-transaction-remove transaction)))
640 (values (manifest-matching-entries manifest remove)
46b23e1a 641 (reverse install) (reverse upgrade) (reverse downgrade))))
79601521
LC
642 ((entry rest ...)
643 ;; Check whether installing ENTRY corresponds to the installation of a
644 ;; new package or to an upgrade.
645
646 ;; XXX: When the exact same output directory is installed, we're not
647 ;; really upgrading anything. Add a check for that case.
648 (let* ((pattern (manifest-entry->pattern entry))
46b23e1a
LC
649 (previous (manifest-lookup manifest pattern))
650 (newer? (and previous
3bea13bb
LC
651 (version>=? (manifest-entry-version entry)
652 (manifest-entry-version previous)))))
79601521 653 (loop rest
ef8993e2 654 (if previous install (cons entry install))
46b23e1a 655 (if (and previous newer?)
ef8993e2 656 (alist-cons previous entry upgrade)
46b23e1a
LC
657 upgrade)
658 (if (and previous (not newer?))
659 (alist-cons previous entry downgrade)
660 downgrade)))))))
79601521 661
343745c8 662(define (manifest-perform-transaction manifest transaction)
c8c25704 663 "Perform TRANSACTION on MANIFEST and return the new manifest."
343745c8
AK
664 (let ((install (manifest-transaction-install transaction))
665 (remove (manifest-transaction-remove transaction)))
666 (manifest-add (manifest-remove manifest remove)
667 install)))
668
343745c8 669\f
cc4ecc2d
LC
670;;;
671;;; Profiles.
672;;;
673
79ee406d 674(define (manifest-inputs manifest)
b4a4bec0 675 "Return a list of <gexp-input> objects for MANIFEST."
55b4715f
LC
676 (define entry->input
677 (match-lambda
678 (($ <manifest-entry> name version output thing deps)
679 ;; THING may be a package or a file name. In the latter case, assume
680 ;; it's already valid.
681 (cons (gexp-input thing output)
682 (append-map entry->input deps)))))
683
684 (append-map entry->input (manifest-entries manifest)))
79ee406d 685
2c9f4786 686(define* (manifest-lookup-package manifest name #:optional version)
d72d7833 687 "Return as a monadic value the first package or store path referenced by
2c9f4786
RW
688MANIFEST that is named NAME and optionally has the given VERSION prefix, or #f
689if not found."
d72d7833
SB
690 ;; Return as a monadic value the package or store path referenced by the
691 ;; manifest ENTRY, or #f if not referenced.
692 (define (entry-lookup-package entry)
693 (define (find-among-inputs inputs)
694 (find (lambda (input)
695 (and (package? input)
2c9f4786
RW
696 (equal? name (package-name input))
697 (if version
698 (string-prefix? version (package-version input))
699 #t)))
d72d7833
SB
700 inputs))
701 (define (find-among-store-items items)
702 (find (lambda (item)
054f60cd 703 (let-values (((name* version*)
2c9f4786
RW
704 (package-name->name+version
705 (store-path-package-name item))))
054f60cd 706 (and (string=? name name*)
2c9f4786 707 (if version
054f60cd 708 (string-prefix? version version*)
2c9f4786 709 #t))))
d72d7833
SB
710 items))
711
d72d7833
SB
712 (with-monad %store-monad
713 (match (manifest-entry-item entry)
714 ((? package? package)
963521a3
SB
715 (match (cons (list (package-name package) package)
716 (package-transitive-inputs package))
d72d7833
SB
717 (((labels inputs . _) ...)
718 (return (find-among-inputs inputs)))))
719 ((? string? item)
720 (mlet %store-monad ((refs (references* item)))
3636b1c7
LC
721 (return (find-among-store-items refs))))
722 (item
723 ;; XXX: ITEM might be a 'computed-file' or anything like that, in
724 ;; which case we don't know what to do. The fix may be to check
725 ;; references once ITEM is compiled, as proposed at
726 ;; <https://bugs.gnu.org/29927>.
727 (return #f)))))
d72d7833
SB
728
729 (anym %store-monad
730 entry-lookup-package (manifest-entries manifest)))
731
79ee406d
LC
732(define (info-dir-file manifest)
733 "Return a derivation that builds the 'dir' file for all the entries of
734MANIFEST."
2f0556ae
LC
735 (define texinfo ;lazy reference
736 (module-ref (resolve-interface '(gnu packages texinfo)) 'texinfo))
737 (define gzip ;lazy reference
738 (module-ref (resolve-interface '(gnu packages compression)) 'gzip))
5b0c648a
LC
739 (define glibc-utf8-locales ;lazy reference
740 (module-ref (resolve-interface '(gnu packages base)) 'glibc-utf8-locales))
2f0556ae 741
79ee406d 742 (define build
99b231de
LC
743 (with-imported-modules '((guix build utils))
744 #~(begin
745 (use-modules (guix build utils)
746 (srfi srfi-1) (srfi srfi-26)
747 (ice-9 ftw))
748
749 (define (info-file? file)
750 (or (string-suffix? ".info" file)
751 (string-suffix? ".info.gz" file)))
752
753 (define (info-files top)
754 (let ((infodir (string-append top "/share/info")))
755 (map (cut string-append infodir "/" <>)
756 (or (scandir infodir info-file?) '()))))
757
5b0c648a
LC
758 (define (info-file-language file)
759 (let* ((base (if (string-suffix? ".gz" file)
760 (basename file ".info.gz")
761 (basename file ".info")))
762 (dot (string-rindex base #\.)))
763 (if dot
764 (string-drop base (+ 1 dot))
765 "en")))
766
99b231de 767 (define (install-info info)
5b0c648a
LC
768 (let ((language (info-file-language info)))
769 ;; We need to choose a valid locale for $LANGUAGE to be honored.
770 (setenv "LC_ALL" "en_US.utf8")
771 (setenv "LANGUAGE" language)
772 (zero?
773 (system* #+(file-append texinfo "/bin/install-info")
774 "--silent" info
775 (apply string-append #$output "/share/info/dir"
776 (if (string=? "en" language)
777 '("")
778 `("." ,language)))))))
779
780 (setenv "PATH" (string-append #+gzip "/bin")) ;for info.gz files
781 (setenv "GUIX_LOCPATH"
782 #+(file-append glibc-utf8-locales "/lib/locale"))
99b231de
LC
783
784 (mkdir-p (string-append #$output "/share/info"))
785 (exit (every install-info
786 (append-map info-files
787 '#$(manifest-inputs manifest)))))))
79ee406d 788
aa46a028 789 (gexp->derivation "info-dir" build
a7a4fd9a
LC
790 #:local-build? #t
791 #:substitutable? #f))
79ee406d 792
042bc828
FB
793(define (ghc-package-cache-file manifest)
794 "Return a derivation that builds the GHC 'package.cache' file for all the
aa46a028 795entries of MANIFEST, or #f if MANIFEST does not have any GHC packages."
99b231de 796 (define ghc ;lazy reference
042bc828
FB
797 (module-ref (resolve-interface '(gnu packages haskell)) 'ghc))
798
799 (define build
99b231de
LC
800 (with-imported-modules '((guix build utils))
801 #~(begin
802 (use-modules (guix build utils)
803 (srfi srfi-1) (srfi srfi-26)
804 (ice-9 ftw))
805
806 (define ghc-name-version
807 (let* ((base (basename #+ghc)))
808 (string-drop base
809 (+ 1 (string-index base #\-)))))
810
811 (define db-subdir
812 (string-append "lib/" ghc-name-version "/package.conf.d"))
813
814 (define db-dir
815 (string-append #$output "/" db-subdir))
816
817 (define (conf-files top)
818 (let ((db (string-append top "/" db-subdir)))
819 (if (file-exists? db)
820 (find-files db "\\.conf$")
821 '())))
822
823 (define (copy-conf-file conf)
824 (let ((base (basename conf)))
825 (copy-file conf (string-append db-dir "/" base))))
826
827 (system* (string-append #+ghc "/bin/ghc-pkg") "init" db-dir)
828 (for-each copy-conf-file
829 (append-map conf-files
830 (delete-duplicates
831 '#$(manifest-inputs manifest))))
832 (let ((success
833 (zero?
834 (system* (string-append #+ghc "/bin/ghc-pkg") "recache"
835 (string-append "--package-db=" db-dir)))))
836 (for-each delete-file (find-files db-dir "\\.conf$"))
837 (exit success)))))
042bc828 838
07eaecfa
LC
839 (with-monad %store-monad
840 ;; Don't depend on GHC when there's nothing to do.
841 (if (any (cut string-prefix? "ghc" <>)
842 (map manifest-entry-name (manifest-entries manifest)))
843 (gexp->derivation "ghc-package-cache" build
a7a4fd9a
LC
844 #:local-build? #t
845 #:substitutable? #f)
07eaecfa 846 (return #f))))
042bc828 847
536c3ee4
MW
848(define (ca-certificate-bundle manifest)
849 "Return a derivation that builds a single-file bundle containing the CA
850certificates in the /etc/ssl/certs sub-directories of the packages in
851MANIFEST. Single-file bundles are required by programs such as Git and Lynx."
852 ;; See <http://lists.gnu.org/archive/html/guix-devel/2015-02/msg00429.html>
853 ;; for a discussion.
854
855 (define glibc-utf8-locales ;lazy reference
856 (module-ref (resolve-interface '(gnu packages base)) 'glibc-utf8-locales))
857
858 (define build
99b231de
LC
859 (with-imported-modules '((guix build utils))
860 #~(begin
861 (use-modules (guix build utils)
862 (rnrs io ports)
863 (srfi srfi-1)
864 (srfi srfi-26)
865 (ice-9 ftw)
866 (ice-9 match))
867
868 (define (pem-file? file)
869 (string-suffix? ".pem" file))
870
871 (define (ca-files top)
872 (let ((cert-dir (string-append top "/etc/ssl/certs")))
873 (map (cut string-append cert-dir "/" <>)
874 (or (scandir cert-dir pem-file?) '()))))
875
876 (define (concatenate-files files result)
877 "Make RESULT the concatenation of all of FILES."
878 (define (dump file port)
879 (display (call-with-input-file file get-string-all)
880 port)
881 (newline port)) ;required, see <https://bugs.debian.org/635570>
882
883 (call-with-output-file result
884 (lambda (port)
885 (for-each (cut dump <> port) files))))
886
887 ;; Some file names in the NSS certificates are UTF-8 encoded so
888 ;; install a UTF-8 locale.
889 (setenv "LOCPATH"
890 (string-append #+glibc-utf8-locales "/lib/locale/"
c6bc8e22
MB
891 #+(version-major+minor
892 (package-version glibc-utf8-locales))))
99b231de
LC
893 (setlocale LC_ALL "en_US.utf8")
894
895 (match (append-map ca-files '#$(manifest-inputs manifest))
896 (()
897 ;; Since there are no CA files, just create an empty directory. Do
898 ;; not create the etc/ssl/certs sub-directory, since that would
899 ;; wrongfully lead to a message about 'SSL_CERT_DIR' needing to be
900 ;; defined.
901 (mkdir #$output)
902 #t)
903 ((ca-files ...)
904 (let ((result (string-append #$output "/etc/ssl/certs")))
905 (mkdir-p result)
906 (concatenate-files ca-files
907 (string-append result
908 "/ca-certificates.crt"))
909 #t))))))
536c3ee4 910
aa46a028 911 (gexp->derivation "ca-certificate-bundle" build
a7a4fd9a
LC
912 #:local-build? #t
913 #:substitutable? #f))
aa46a028 914
de136f3e
DM
915(define (glib-schemas manifest)
916 "Return a derivation that unions all schemas from manifest entries and
917creates the Glib 'gschemas.compiled' file."
918 (define glib ; lazy reference
919 (module-ref (resolve-interface '(gnu packages glib)) 'glib))
920
921 (mlet %store-monad ((%glib (manifest-lookup-package manifest "glib"))
922 ;; XXX: Can't use glib-compile-schemas corresponding
923 ;; to the glib referenced by 'manifest'. Because
924 ;; '%glib' can be either a package or store path, and
925 ;; there's no way to get the "bin" output for the later.
926 (glib-compile-schemas
927 -> #~(string-append #+glib:bin
928 "/bin/glib-compile-schemas")))
929
930 (define build
931 (with-imported-modules '((guix build utils)
932 (guix build union)
933 (guix build profiles)
934 (guix search-paths)
935 (guix records))
936 #~(begin
937 (use-modules (guix build utils)
938 (guix build union)
939 (guix build profiles)
940 (srfi srfi-26))
941
942 (let* ((destdir (string-append #$output "/share/glib-2.0/schemas"))
943 (schemadirs (filter file-exists?
944 (map (cut string-append <> "/share/glib-2.0/schemas")
945 '#$(manifest-inputs manifest)))))
946
947 ;; Union all the schemas.
948 (mkdir-p (string-append #$output "/share/glib-2.0"))
949 (union-build destdir schemadirs
950 #:log-port (%make-void-port "w"))
951
952 (let ((dir destdir))
953 (when (file-is-directory? dir)
954 (ensure-writable-directory dir)
955 (invoke #+glib-compile-schemas
956 (string-append "--targetdir=" dir)
957 dir)))))))
958
959 ;; Don't run the hook when there's nothing to do.
960 (if %glib
961 (gexp->derivation "glib-schemas" build
962 #:local-build? #t
963 #:substitutable? #f)
964 (return #f))))
965
b04af0ec
SB
966(define (gtk-icon-themes manifest)
967 "Return a derivation that unions all icon themes from manifest entries and
968creates the GTK+ 'icon-theme.cache' file for each theme."
d1fb4af6
SB
969 (define gtk+ ; lazy reference
970 (module-ref (resolve-interface '(gnu packages gtk)) 'gtk+))
971
972 (mlet %store-monad ((%gtk+ (manifest-lookup-package manifest "gtk+"))
973 ;; XXX: Can't use gtk-update-icon-cache corresponding
974 ;; to the gtk+ referenced by 'manifest'. Because
975 ;; '%gtk+' can be either a package or store path, and
976 ;; there's no way to get the "bin" output for the later.
977 (gtk-update-icon-cache
978 -> #~(string-append #+gtk+:bin
979 "/bin/gtk-update-icon-cache")))
980
b04af0ec 981 (define build
99b231de
LC
982 (with-imported-modules '((guix build utils)
983 (guix build union)
984 (guix build profiles)
985 (guix search-paths)
986 (guix records))
987 #~(begin
988 (use-modules (guix build utils)
989 (guix build union)
990 (guix build profiles)
991 (srfi srfi-26)
992 (ice-9 ftw))
993
994 (let* ((destdir (string-append #$output "/share/icons"))
995 (icondirs (filter file-exists?
996 (map (cut string-append <> "/share/icons")
d1fb4af6 997 '#$(manifest-inputs manifest)))))
99b231de
LC
998
999 ;; Union all the icons.
1000 (mkdir-p (string-append #$output "/share"))
1001 (union-build destdir icondirs
1002 #:log-port (%make-void-port "w"))
1003
1004 ;; Update the 'icon-theme.cache' file for each icon theme.
1005 (for-each
1006 (lambda (theme)
1007 (let ((dir (string-append destdir "/" theme)))
1008 ;; Occasionally DESTDIR contains plain files, such as
1009 ;; "abiword_48.png". Ignore these.
1010 (when (file-is-directory? dir)
1011 (ensure-writable-directory dir)
d1fb4af6 1012 (system* #+gtk-update-icon-cache "-t" dir "--quiet"))))
99b231de 1013 (scandir destdir (negate (cut member <> '("." "..")))))))))
b04af0ec
SB
1014
1015 ;; Don't run the hook when there's nothing to do.
d1fb4af6 1016 (if %gtk+
b04af0ec 1017 (gexp->derivation "gtk-icon-themes" build
a7a4fd9a
LC
1018 #:local-build? #t
1019 #:substitutable? #f)
b04af0ec
SB
1020 (return #f))))
1021
7ddc1780
RW
1022(define (gtk-im-modules manifest)
1023 "Return a derivation that builds the cache files for input method modules
1024for both major versions of GTK+."
1025
1026 (mlet %store-monad ((gtk+ (manifest-lookup-package manifest "gtk+" "3"))
1027 (gtk+-2 (manifest-lookup-package manifest "gtk+" "2")))
1028
06d7d119 1029 (define (build gtk gtk-version query)
7ddc1780
RW
1030 (let ((major (string-take gtk-version 1)))
1031 (with-imported-modules '((guix build utils)
1032 (guix build union)
1033 (guix build profiles)
1034 (guix search-paths)
1035 (guix records))
1036 #~(begin
1037 (use-modules (guix build utils)
1038 (guix build union)
1039 (guix build profiles)
1040 (ice-9 popen)
1041 (srfi srfi-1)
1042 (srfi srfi-26))
1043
1044 (let* ((prefix (string-append "/lib/gtk-" #$major ".0/"
1045 #$gtk-version))
7ddc1780
RW
1046 (destdir (string-append #$output prefix))
1047 (moddirs (cons (string-append #$gtk prefix "/immodules")
1048 (filter file-exists?
1049 (map (cut string-append <> prefix "/immodules")
1050 '#$(manifest-inputs manifest)))))
1051 (modules (append-map (cut find-files <> "\\.so$")
1052 moddirs)))
1053
1054 ;; Generate a new immodules cache file.
1055 (mkdir-p (string-append #$output prefix))
06d7d119 1056 (let ((pipe (apply open-pipe* OPEN_READ #$query modules))
7ddc1780
RW
1057 (outfile (string-append #$output prefix
1058 "/immodules-gtk" #$major ".cache")))
1059 (dynamic-wind
1060 (const #t)
1061 (lambda ()
1062 (call-with-output-file outfile
1063 (lambda (out)
1064 (while (not (eof-object? (peek-char pipe)))
1065 (write-char (read-char pipe) out))))
1066 #t)
1067 (lambda ()
1068 (close-pipe pipe)))))))))
1069
1070 ;; Don't run the hook when there's nothing to do.
06d7d119
YH
1071 (let* ((pkg-gtk+ (module-ref ; lazy reference
1072 (resolve-interface '(gnu packages gtk)) 'gtk+))
1073 (gexp #~(begin
1074 #$(if gtk+
1075 (build
1076 gtk+ "3.0.0"
1077 ;; Use 'gtk-query-immodules-3.0' from the 'bin'
1078 ;; output of latest gtk+ package.
1079 #~(string-append
1080 #$pkg-gtk+:bin "/bin/gtk-query-immodules-3.0"))
1081 #t)
1082 #$(if gtk+-2
1083 (build
1084 gtk+-2 "2.10.0"
1085 #~(string-append
1086 #$gtk+-2 "/bin/gtk-query-immodules-2.0"))
1087 #t))))
7ddc1780
RW
1088 (if (or gtk+ gtk+-2)
1089 (gexp->derivation "gtk-im-modules" gexp
1090 #:local-build? #t
1091 #:substitutable? #f)
1092 (return #f)))))
1093
842cb820
SB
1094(define (xdg-desktop-database manifest)
1095 "Return a derivation that builds the @file{mimeinfo.cache} database from
1096desktop files. It's used to query what applications can handle a given
1097MIME type."
85cfbd46
SB
1098 (define desktop-file-utils ; lazy reference
1099 (module-ref (resolve-interface '(gnu packages freedesktop))
1100 'desktop-file-utils))
1101
1102 (mlet %store-monad ((glib
d72d7833 1103 (manifest-lookup-package
85cfbd46 1104 manifest "glib")))
d72d7833 1105 (define build
99b231de
LC
1106 (with-imported-modules '((guix build utils)
1107 (guix build union))
1108 #~(begin
1109 (use-modules (srfi srfi-26)
1110 (guix build utils)
1111 (guix build union))
1112 (let* ((destdir (string-append #$output "/share/applications"))
1113 (appdirs (filter file-exists?
1114 (map (cut string-append <>
1115 "/share/applications")
1116 '#$(manifest-inputs manifest))))
1117 (update-desktop-database (string-append
1118 #+desktop-file-utils
1119 "/bin/update-desktop-database")))
1120 (mkdir-p (string-append #$output "/share"))
1121 (union-build destdir appdirs
1122 #:log-port (%make-void-port "w"))
1123 (exit (zero? (system* update-desktop-database destdir)))))))
842cb820 1124
85cfbd46
SB
1125 ;; Don't run the hook when 'glib' is not referenced.
1126 (if glib
d72d7833 1127 (gexp->derivation "xdg-desktop-database" build
d72d7833
SB
1128 #:local-build? #t
1129 #:substitutable? #f)
1130 (return #f))))
842cb820 1131
6c06b1fd
SB
1132(define (xdg-mime-database manifest)
1133 "Return a derivation that builds the @file{mime.cache} database from manifest
1134entries. It's used to query the MIME type of a given file."
801d316b
SB
1135 (define shared-mime-info ; lazy reference
1136 (module-ref (resolve-interface '(gnu packages gnome)) 'shared-mime-info))
1137
1138 (mlet %store-monad ((glib
d72d7833 1139 (manifest-lookup-package
801d316b 1140 manifest "glib")))
d72d7833 1141 (define build
99b231de
LC
1142 (with-imported-modules '((guix build utils)
1143 (guix build union))
1144 #~(begin
1145 (use-modules (srfi srfi-26)
1146 (guix build utils)
1147 (guix build union))
1148 (let* ((datadir (string-append #$output "/share"))
1149 (destdir (string-append datadir "/mime"))
1150 (pkgdirs (filter file-exists?
1151 (map (cut string-append <>
1152 "/share/mime/packages")
801d316b
SB
1153 (cons #+shared-mime-info
1154 '#$(manifest-inputs manifest)))))
99b231de
LC
1155 (update-mime-database (string-append
1156 #+shared-mime-info
1157 "/bin/update-mime-database")))
1158 (mkdir-p destdir)
1159 (union-build (string-append destdir "/packages") pkgdirs
1160 #:log-port (%make-void-port "w"))
1161 (setenv "XDG_DATA_HOME" datadir)
1162 (exit (zero? (system* update-mime-database destdir)))))))
d72d7833 1163
801d316b
SB
1164 ;; Don't run the hook when there are no GLib based applications.
1165 (if glib
d72d7833 1166 (gexp->derivation "xdg-mime-database" build
d72d7833
SB
1167 #:local-build? #t
1168 #:substitutable? #f)
1169 (return #f))))
6c06b1fd 1170
0a5ce0d1
HY
1171;; Several font packages may install font files into same directory, so
1172;; fonts.dir and fonts.scale file should be generated here, instead of in
1173;; packages.
9eb5a449
AK
1174(define (fonts-dir-file manifest)
1175 "Return a derivation that builds the @file{fonts.dir} and @file{fonts.scale}
0a5ce0d1 1176files for the fonts of the @var{manifest} entries."
9eb5a449
AK
1177 (define mkfontscale
1178 (module-ref (resolve-interface '(gnu packages xorg)) 'mkfontscale))
1179
1180 (define mkfontdir
1181 (module-ref (resolve-interface '(gnu packages xorg)) 'mkfontdir))
1182
1183 (define build
1184 #~(begin
1185 (use-modules (srfi srfi-26)
1186 (guix build utils)
1187 (guix build union))
0a5ce0d1
HY
1188 (let ((fonts-dirs (filter file-exists?
1189 (map (cut string-append <>
1190 "/share/fonts")
1191 '#$(manifest-inputs manifest)))))
9eb5a449 1192 (mkdir #$output)
0a5ce0d1 1193 (if (null? fonts-dirs)
9eb5a449 1194 (exit #t)
0a5ce0d1
HY
1195 (let* ((share-dir (string-append #$output "/share"))
1196 (fonts-dir (string-append share-dir "/fonts"))
9eb5a449
AK
1197 (mkfontscale (string-append #+mkfontscale
1198 "/bin/mkfontscale"))
1199 (mkfontdir (string-append #+mkfontdir
0a5ce0d1
HY
1200 "/bin/mkfontdir"))
1201 (empty-file? (lambda (filename)
1202 (call-with-ascii-input-file filename
1203 (lambda (p)
1204 (eqv? #\0 (read-char p))))))
1205 (fonts-dir-file "fonts.dir")
1206 (fonts-scale-file "fonts.scale"))
1207 (mkdir-p share-dir)
1208 ;; Create all sub-directories, because we may create fonts.dir
1209 ;; and fonts.scale files in the sub-directories.
1210 (union-build fonts-dir fonts-dirs
1211 #:log-port (%make-void-port "w")
1212 #:create-all-directories? #t)
1213 (let ((directories (find-files fonts-dir
1214 (lambda (file stat)
1215 (eq? 'directory (stat:type stat)))
1216 #:directories? #t)))
1217 (for-each (lambda (dir)
1218 (with-directory-excursion dir
1219 (when (file-exists? fonts-scale-file)
1220 (delete-file fonts-scale-file))
1221 (when (file-exists? fonts-dir-file)
1222 (delete-file fonts-dir-file))
1223 (unless (and (zero? (system* mkfontscale))
1224 (zero? (system* mkfontdir)))
1225 (exit #f))
32b7506c
RW
1226 (when (and (file-exists? fonts-scale-file)
1227 (empty-file? fonts-scale-file))
0a5ce0d1 1228 (delete-file fonts-scale-file))
32b7506c
RW
1229 (when (and (file-exists? fonts-dir-file)
1230 (empty-file? fonts-dir-file))
0a5ce0d1
HY
1231 (delete-file fonts-dir-file))))
1232 directories)))))))
9eb5a449
AK
1233
1234 (gexp->derivation "fonts-dir" build
1235 #:modules '((guix build utils)
0a5ce0d1
HY
1236 (guix build union)
1237 (srfi srfi-26))
9eb5a449
AK
1238 #:local-build? #t
1239 #:substitutable? #f))
1240
a0b87ef8
MC
1241(define (manual-database manifest)
1242 "Return a derivation that builds the manual page database (\"mandb\") for
1243the entries in MANIFEST."
b8396f96
LC
1244 (define gdbm-ffi
1245 (module-ref (resolve-interface '(gnu packages guile))
1246 'guile-gdbm-ffi))
1247
1248 (define zlib
1249 (module-ref (resolve-interface '(gnu packages compression)) 'zlib))
1250
1251 (define config.scm
1252 (scheme-file "config.scm"
1253 #~(begin
88d9eccc 1254 (define-module #$'(guix config) ;placate Geiser
b8396f96
LC
1255 #:export (%libz))
1256
1257 (define %libz
1258 #+(file-append zlib "/lib/libz")))))
1259
1260 (define modules
1261 (cons `((guix config) => ,config.scm)
1262 (delete '(guix config)
1263 (source-module-closure `((guix build utils)
1264 (guix man-db))))))
a0b87ef8
MC
1265
1266 (define build
b8396f96 1267 (with-imported-modules modules
331ac4cc
LC
1268 (with-extensions (list gdbm-ffi) ;for (guix man-db)
1269 #~(begin
1270 (use-modules (guix man-db)
1271 (guix build utils)
1272 (srfi srfi-1)
1273 (srfi srfi-19))
1274
1275 (define (compute-entries)
1276 (append-map (lambda (directory)
1277 (let ((man (string-append directory "/share/man")))
1278 (if (directory-exists? man)
1279 (mandb-entries man)
1280 '())))
1281 '#$(manifest-inputs manifest)))
1282
1283 (define man-directory
1284 (string-append #$output "/share/man"))
1285
1286 (mkdir-p man-directory)
1287
1288 (format #t "Creating manual page database...~%")
1289 (force-output)
1290 (let* ((start (current-time))
1291 (entries (compute-entries))
1292 (_ (write-mandb-database (string-append man-directory
1293 "/index.db")
1294 entries))
1295 (duration (time-difference (current-time) start)))
1296 (format #t "~a entries processed in ~,1f s~%"
1297 (length entries)
1298 (+ (time-second duration)
1299 (* (time-nanosecond duration) (expt 10 -9))))
1300 (force-output))))))
a0b87ef8
MC
1301
1302 (gexp->derivation "manual-database" build
b8396f96
LC
1303
1304 ;; Work around GDBM 1.13 issue whereby uninitialized bytes
1305 ;; get written to disk:
1306 ;; <https://debbugs.gnu.org/cgi/bugreport.cgi?bug=29654#23>.
1307 #:env-vars `(("MALLOC_PERTURB_" . "1"))
1308
a0b87ef8
MC
1309 #:local-build? #t))
1310
aa46a028
LC
1311(define %default-profile-hooks
1312 ;; This is the list of derivation-returning procedures that are called by
1313 ;; default when making a non-empty profile.
1314 (list info-dir-file
a0b87ef8 1315 manual-database
9eb5a449 1316 fonts-dir-file
aa46a028 1317 ghc-package-cache-file
b04af0ec 1318 ca-certificate-bundle
de136f3e 1319 glib-schemas
842cb820 1320 gtk-icon-themes
7ddc1780 1321 gtk-im-modules
6c06b1fd
SB
1322 xdg-desktop-database
1323 xdg-mime-database))
536c3ee4
MW
1324
1325(define* (profile-derivation manifest
1326 #:key
e5f04c2d 1327 (hooks %default-profile-hooks)
a6562c7e 1328 (locales? #t)
afd06f60 1329 (allow-collisions? #f)
e00ade3f 1330 (relative-symlinks? #f)
176febe3 1331 system target)
79ee406d 1332 "Return a derivation that builds a profile (aka. 'user environment') with
aa46a028 1333the given MANIFEST. The profile includes additional derivations returned by
a6562c7e 1334the monadic procedures listed in HOOKS--such as an Info 'dir' file, etc.
afd06f60
LC
1335Unless ALLOW-COLLISIONS? is true, a '&profile-collision-error' is raised if
1336entries in MANIFEST collide (for instance if there are two same-name packages
1337with a different version number.)
a6562c7e
LC
1338
1339When LOCALES? is true, the build is performed under a UTF-8 locale; this adds
176febe3
LC
1340a dependency on the 'glibc-utf8-locales' package.
1341
e00ade3f
LC
1342When RELATIVE-SYMLINKS? is true, use relative file names for symlink targets.
1343This is one of the things to do for the result to be relocatable.
1344
176febe3
LC
1345When TARGET is true, it must be a GNU triplet, and the packages in MANIFEST
1346are cross-built for TARGET."
a654dc4b
LC
1347 (mlet* %store-monad ((system (if system
1348 (return system)
1349 (current-system)))
afd06f60
LC
1350 (ok? (if allow-collisions?
1351 (return #t)
1352 (check-for-collisions manifest system
1353 #:target target)))
a654dc4b
LC
1354 (extras (if (null? (manifest-entries manifest))
1355 (return '())
1356 (sequence %store-monad
1357 (map (lambda (hook)
1358 (hook manifest))
1359 hooks)))))
79ee406d 1360 (define inputs
eeae0b3c
SB
1361 (append (filter-map (lambda (drv)
1362 (and (derivation? drv)
1363 (gexp-input drv)))
07eaecfa 1364 extras)
536c3ee4 1365 (manifest-inputs manifest)))
79ee406d 1366
1af0860e
LC
1367 (define glibc-utf8-locales ;lazy reference
1368 (module-ref (resolve-interface '(gnu packages base))
1369 'glibc-utf8-locales))
1370
a6562c7e
LC
1371 (define set-utf8-locale
1372 ;; Some file names (e.g., in 'nss-certs') are UTF-8 encoded so
1373 ;; install a UTF-8 locale.
1374 #~(begin
1375 (setenv "LOCPATH"
1376 #$(file-append glibc-utf8-locales "/lib/locale/"
c6bc8e22
MB
1377 (version-major+minor
1378 (package-version glibc-utf8-locales))))
a6562c7e
LC
1379 (setlocale LC_ALL "en_US.utf8")))
1380
79ee406d 1381 (define builder
99b231de
LC
1382 (with-imported-modules '((guix build profiles)
1383 (guix build union)
1384 (guix build utils)
1385 (guix search-paths)
1386 (guix records))
1387 #~(begin
1388 (use-modules (guix build profiles)
1389 (guix search-paths)
1390 (srfi srfi-1))
1391
1392 (setvbuf (current-output-port) _IOLBF)
1393 (setvbuf (current-error-port) _IOLBF)
1394
a6562c7e 1395 #+(if locales? set-utf8-locale #t)
1af0860e 1396
99b231de
LC
1397 (define search-paths
1398 ;; Search paths of MANIFEST's packages, converted back to their
1399 ;; record form.
1400 (map sexp->search-path-specification
1401 (delete-duplicates
1402 '#$(map search-path-specification->sexp
f03df3ee 1403 (manifest-search-paths manifest)))))
99b231de
LC
1404
1405 (build-profile #$output '#$inputs
e00ade3f
LC
1406 #:symlink #$(if relative-symlinks?
1407 #~symlink-relative
1408 #~symlink)
99b231de
LC
1409 #:manifest '#$(manifest->gexp manifest)
1410 #:search-paths search-paths))))
79ee406d
LC
1411
1412 (gexp->derivation "profile" builder
40d71e44 1413 #:system system
176febe3 1414 #:target target
a7a4fd9a 1415
cbb76780
LC
1416 ;; Don't complain about _IO* on Guile 2.2.
1417 #:env-vars '(("GUILE_WARN_DEPRECATED" . "no"))
1418
a7a4fd9a
LC
1419 ;; Not worth offloading.
1420 #:local-build? #t
1421
1422 ;; Disable substitution because it would trigger a
1423 ;; connection to the substitute server, which is likely
1424 ;; to have no substitute to offer.
1425 #:substitutable? #f)))
cc4ecc2d 1426
78d55b70
LC
1427(define* (profile-search-paths profile
1428 #:optional (manifest (profile-manifest profile))
1429 #:key (getenv (const #f)))
1430 "Read the manifest of PROFILE and evaluate the values of search path
1431environment variables required by PROFILE; return a list of
1432specification/value pairs. If MANIFEST is not #f, it is assumed to be the
1433manifest of PROFILE, which avoids rereading it.
1434
1435Use GETENV to determine the current settings and report only settings not
1436already effective."
1437 (evaluate-search-paths (manifest-search-paths manifest)
1438 (list profile) getenv))
1439
cc4ecc2d
LC
1440(define (profile-regexp profile)
1441 "Return a regular expression that matches PROFILE's name and number."
1442 (make-regexp (string-append "^" (regexp-quote (basename profile))
1443 "-([0-9]+)")))
1444
1445(define (generation-number profile)
1446 "Return PROFILE's number or 0. An absolute file name must be used."
1447 (or (and=> (false-if-exception (regexp-exec (profile-regexp profile)
1448 (basename (readlink profile))))
1449 (compose string->number (cut match:substring <> 1)))
1450 0))
1451
1452(define (generation-numbers profile)
1453 "Return the sorted list of generation numbers of PROFILE, or '(0) if no
1454former profiles were found."
cc4ecc2d
LC
1455 (match (scandir (dirname profile)
1456 (cute regexp-exec (profile-regexp profile) <>))
1457 (#f ; no profile directory
1458 '(0))
1459 (() ; no profiles
1460 '(0))
1461 ((profiles ...) ; former profiles around
1462 (sort (map (compose string->number
1463 (cut match:substring <> 1)
1464 (cute regexp-exec (profile-regexp profile) <>))
1465 profiles)
1466 <))))
1467
f452e8ff
AK
1468(define (profile-generations profile)
1469 "Return a list of PROFILE's generations."
1470 (let ((generations (generation-numbers profile)))
1471 (if (equal? generations '(0))
1472 '()
1473 generations)))
1474
9008debc
CM
1475(define (relative-generation-spec->number profile spec)
1476 "Return PROFILE's generation specified by SPEC, which is a string. The SPEC
1477may be a N, -N, or +N, where N is a number. If the spec is N, then the number
1478returned is N. If it is -N, then the number returned is the profile's current
1479generation number minus N. If it is +N, then the number returned is the
1480profile's current generation number plus N. Return #f if there is no such
1481generation."
1482 (let ((number (string->number spec)))
1483 (and number
1484 (case (string-ref spec 0)
1485 ((#\+ #\-)
1486 (relative-generation profile number))
1487 (else (if (memv number (profile-generations profile))
1488 number
1489 #f))))))
1490
1491
3ccde087
AK
1492(define* (relative-generation profile shift #:optional
1493 (current (generation-number profile)))
1494 "Return PROFILE's generation shifted from the CURRENT generation by SHIFT.
1495SHIFT is a positive or negative number.
1496Return #f if there is no such generation."
1497 (let* ((abs-shift (abs shift))
1498 (numbers (profile-generations profile))
1499 (from-current (memq current
1500 (if (negative? shift)
1501 (reverse numbers)
1502 numbers))))
1503 (and from-current
1504 (< abs-shift (length from-current))
1505 (list-ref from-current abs-shift))))
1506
1507(define* (previous-generation-number profile #:optional
1508 (number (generation-number profile)))
cc4ecc2d
LC
1509 "Return the number of the generation before generation NUMBER of
1510PROFILE, or 0 if none exists. It could be NUMBER - 1, but it's not the
1511case when generations have been deleted (there are \"holes\")."
3ccde087
AK
1512 (or (relative-generation profile -1 number)
1513 0))
cc4ecc2d
LC
1514
1515(define (generation-file-name profile generation)
1516 "Return the file name for PROFILE's GENERATION."
1517 (format #f "~a-~a-link" profile generation))
1518
1519(define (generation-time profile number)
1520 "Return the creation time of a generation in the UTC format."
1521 (make-time time-utc 0
1522 (stat:ctime (stat (generation-file-name profile number)))))
1523
06d45f45
LC
1524(define (link-to-empty-profile store generation)
1525 "Link GENERATION, a string, to the empty profile. An error is raised if
1526that fails."
1527 (let* ((drv (run-with-store store
a6562c7e
LC
1528 (profile-derivation (manifest '())
1529 #:locales? #f)))
06d45f45
LC
1530 (prof (derivation->output-path drv "out")))
1531 (build-derivations store (list drv))
1532 (switch-symlinks generation prof)))
1533
1534(define (switch-to-generation profile number)
1535 "Atomically switch PROFILE to the generation NUMBER. Return the number of
1536the generation that was current before switching."
1537 (let ((current (generation-number profile))
1538 (generation (generation-file-name profile number)))
1539 (cond ((not (file-exists? profile))
1540 (raise (condition (&profile-not-found-error
1541 (profile profile)))))
1542 ((not (file-exists? generation))
1543 (raise (condition (&missing-generation-error
1544 (profile profile)
1545 (generation number)))))
1546 (else
bc6e291e 1547 (switch-symlinks profile (basename generation))
06d45f45
LC
1548 current))))
1549
1550(define (switch-to-previous-generation profile)
1551 "Atomically switch PROFILE to the previous generation. Return the former
1552generation number and the current one."
1553 (let ((previous (previous-generation-number profile)))
1554 (values (switch-to-generation profile previous)
1555 previous)))
1556
1557(define (roll-back store profile)
1558 "Roll back to the previous generation of PROFILE. Return the number of the
1559generation that was current before switching and the new generation number."
1560 (let* ((number (generation-number profile))
1561 (previous-number (previous-generation-number profile number))
1562 (previous-generation (generation-file-name profile previous-number)))
1563 (cond ((not (file-exists? profile)) ;invalid profile
1564 (raise (condition (&profile-not-found-error
1565 (profile profile)))))
1566 ((zero? number) ;empty profile
1567 (values number number))
1568 ((or (zero? previous-number) ;going to emptiness
1569 (not (file-exists? previous-generation)))
1570 (link-to-empty-profile store previous-generation)
1571 (switch-to-previous-generation profile))
1572 (else ;anything else
1573 (switch-to-previous-generation profile)))))
1574
1575(define (delete-generation store profile number)
1576 "Delete generation with NUMBER from PROFILE. Return the file name of the
1577generation that has been deleted, or #f if nothing was done (for instance
1578because the NUMBER is zero.)"
1579 (define (delete-and-return)
1580 (let ((generation (generation-file-name profile number)))
1581 (delete-file generation)
1582 generation))
1583
1584 (let* ((current-number (generation-number profile))
1585 (previous-number (previous-generation-number profile number))
1586 (previous-generation (generation-file-name profile previous-number)))
1587 (cond ((zero? number) #f) ;do not delete generation 0
1588 ((and (= number current-number)
1589 (not (file-exists? previous-generation)))
1590 (link-to-empty-profile store previous-generation)
1591 (switch-to-previous-generation profile)
1592 (delete-and-return))
1593 ((= number current-number)
1594 (roll-back store profile)
1595 (delete-and-return))
1596 (else
1597 (delete-and-return)))))
1598
efcb4441
LC
1599(define %user-profile-directory
1600 (and=> (getenv "HOME")
1601 (cut string-append <> "/.guix-profile")))
1602
1603(define %profile-directory
1604 (string-append %state-directory "/profiles/"
1605 (or (and=> (or (getenv "USER")
1606 (getenv "LOGNAME"))
1607 (cut string-append "per-user/" <>))
1608 "default")))
1609
1610(define %current-profile
1611 ;; Call it `guix-profile', not `profile', to allow Guix profiles to
1612 ;; coexist with Nix profiles.
1613 (string-append %profile-directory "/guix-profile"))
1614
77dcfb4c
LC
1615(define (ensure-profile-directory)
1616 "Attempt to create /…/profiles/per-user/$USER if needed."
1617 (let ((s (stat %profile-directory #f)))
1618 (unless (and s (eq? 'directory (stat:type s)))
1619 (catch 'system-error
1620 (lambda ()
1621 (mkdir-p %profile-directory))
1622 (lambda args
1623 ;; Often, we cannot create %PROFILE-DIRECTORY because its
1624 ;; parent directory is root-owned and we're running
1625 ;; unprivileged.
1626 (raise (condition
1627 (&message
1628 (message
1629 (format #f
1630 (G_ "while creating directory `~a': ~a")
1631 %profile-directory
1632 (strerror (system-error-errno args)))))
1633 (&fix-hint
1634 (hint
1635 (format #f (G_ "Please create the @file{~a} directory, \
1636with you as the owner.")
1637 %profile-directory))))))))
1638
1639 ;; Bail out if it's not owned by the user.
1640 (unless (or (not s) (= (stat:uid s) (getuid)))
1641 (raise (condition
1642 (&message
1643 (message
1644 (format #f (G_ "directory `~a' is not owned by you")
1645 %profile-directory)))
1646 (&fix-hint
1647 (hint
1648 (format #f (G_ "Please change the owner of @file{~a} \
1649to user ~s.")
1650 %profile-directory (or (getenv "USER")
1651 (getenv "LOGNAME")
1652 (getuid))))))))))
1653
efcb4441 1654(define (canonicalize-profile profile)
50c72ecd
LC
1655 "If PROFILE points to a profile in %PROFILE-DIRECTORY, return that.
1656Otherwise return PROFILE unchanged. The goal is to treat '-p ~/.guix-profile'
1657as if '-p' was omitted." ; see <http://bugs.gnu.org/17939>
1658 ;; Trim trailing slashes so 'readlink' can do its job.
efcb4441 1659 (let ((profile (string-trim-right profile #\/)))
50c72ecd
LC
1660 (catch 'system-error
1661 (lambda ()
1662 (let ((target (readlink profile)))
1663 (if (string=? (dirname target) %profile-directory)
1664 target
1665 profile)))
1666 (const profile))))
efcb4441 1667
1c795c4f
LC
1668(define %known-shorthand-profiles
1669 ;; Known shorthand forms for profiles that the user manipulates.
1670 (list (string-append (config-directory #:ensure? #f) "/current")
1671 %user-profile-directory))
1672
efcb4441 1673(define (user-friendly-profile profile)
1c795c4f
LC
1674 "Return either ~/.guix-profile or ~/.config/guix/current if that's what
1675PROFILE refers to, directly or indirectly, or PROFILE."
1676 (or (find (lambda (shorthand)
1677 (and shorthand
1678 (let ((target (false-if-exception
1679 (readlink shorthand))))
1680 (and target (string=? target profile)))))
1681 %known-shorthand-profiles)
efcb4441
LC
1682 profile))
1683
cc4ecc2d 1684;;; profiles.scm ends here