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