doc: Update "Installing Guix from Guix".
[jackhill/guix/guix.git] / guix / profiles.scm
CommitLineData
cc4ecc2d 1;;; GNU Guix --- Functional package management for GNU
9e55f04a 2;;; Copyright © 2013, 2014 Ludovic Courtès <ludo@gnu.org>
cc4ecc2d 3;;; Copyright © 2013 Nikita Karetnikov <nikita@karetnikov.org>
343745c8 4;;; Copyright © 2014 Alex Kost <alezost@gmail.com>
cc4ecc2d
LC
5;;;
6;;; This file is part of GNU Guix.
7;;;
8;;; GNU Guix is free software; you can redistribute it and/or modify it
9;;; under the terms of the GNU General Public License as published by
10;;; the Free Software Foundation; either version 3 of the License, or (at
11;;; your option) any later version.
12;;;
13;;; GNU Guix is distributed in the hope that it will be useful, but
14;;; WITHOUT ANY WARRANTY; without even the implied warranty of
15;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16;;; GNU General Public License for more details.
17;;;
18;;; You should have received a copy of the GNU General Public License
19;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
20
21(define-module (guix profiles)
343745c8 22 #:use-module (guix ui)
cc4ecc2d
LC
23 #:use-module (guix utils)
24 #:use-module (guix records)
25 #:use-module (guix derivations)
26 #:use-module (guix packages)
a54c94a4 27 #:use-module (guix gexp)
79ee406d 28 #:use-module (guix monads)
cc4ecc2d
LC
29 #:use-module (ice-9 match)
30 #:use-module (ice-9 regex)
31 #:use-module (ice-9 ftw)
343745c8 32 #:use-module (ice-9 format)
cc4ecc2d
LC
33 #:use-module (srfi srfi-1)
34 #:use-module (srfi srfi-9)
79601521 35 #:use-module (srfi srfi-11)
cc4ecc2d
LC
36 #:use-module (srfi srfi-19)
37 #:use-module (srfi srfi-26)
38 #:export (manifest make-manifest
39 manifest?
40 manifest-entries
41
42 <manifest-entry> ; FIXME: eventually make it internal
43 manifest-entry
44 manifest-entry?
45 manifest-entry-name
46 manifest-entry-version
47 manifest-entry-output
a54c94a4 48 manifest-entry-item
cc4ecc2d
LC
49 manifest-entry-dependencies
50
a2078770
LC
51 manifest-pattern
52 manifest-pattern?
53
cc4ecc2d 54 manifest-remove
f7554030 55 manifest-add
cc4ecc2d 56 manifest-installed?
a2078770 57 manifest-matching-entries
cc4ecc2d 58
343745c8
AK
59 manifest-transaction
60 manifest-transaction?
61 manifest-transaction-install
62 manifest-transaction-remove
63 manifest-perform-transaction
79601521 64 manifest-transaction-effects
343745c8
AK
65 manifest-show-transaction
66
cc4ecc2d 67 profile-manifest
462f5cca 68 package->manifest-entry
cc4ecc2d
LC
69 profile-derivation
70 generation-number
71 generation-numbers
72 previous-generation-number
73 generation-time
74 generation-file-name))
75
76;;; Commentary:
77;;;
78;;; Tools to create and manipulate profiles---i.e., the representation of a
79;;; set of installed packages.
80;;;
81;;; Code:
82
83\f
84;;;
85;;; Manifests.
86;;;
87
88(define-record-type <manifest>
89 (manifest entries)
90 manifest?
91 (entries manifest-entries)) ; list of <manifest-entry>
92
93;; Convenient alias, to avoid name clashes.
94(define make-manifest manifest)
95
96(define-record-type* <manifest-entry> manifest-entry
97 make-manifest-entry
98 manifest-entry?
99 (name manifest-entry-name) ; string
100 (version manifest-entry-version) ; string
101 (output manifest-entry-output ; string
102 (default "out"))
a54c94a4 103 (item manifest-entry-item) ; package | store path
4ca0b410
LC
104 (dependencies manifest-entry-dependencies ; (store path | package)*
105 (default '())))
cc4ecc2d 106
a2078770
LC
107(define-record-type* <manifest-pattern> manifest-pattern
108 make-manifest-pattern
109 manifest-pattern?
110 (name manifest-pattern-name) ; string
111 (version manifest-pattern-version ; string | #f
112 (default #f))
113 (output manifest-pattern-output ; string | #f
114 (default "out")))
115
cc4ecc2d
LC
116(define (profile-manifest profile)
117 "Return the PROFILE's manifest."
118 (let ((file (string-append profile "/manifest")))
119 (if (file-exists? file)
120 (call-with-input-file file read-manifest)
121 (manifest '()))))
122
462f5cca
LC
123(define* (package->manifest-entry package #:optional output)
124 "Return a manifest entry for the OUTPUT of package PACKAGE. When OUTPUT is
125omitted or #f, use the first output of PACKAGE."
126 (let ((deps (map (match-lambda
127 ((label package)
128 `(,package "out"))
129 ((label package output)
130 `(,package ,output)))
131 (package-transitive-propagated-inputs package))))
132 (manifest-entry
133 (name (package-name package))
134 (version (package-version package))
135 (output (or output (car (package-outputs package))))
136 (item package)
137 (dependencies (delete-duplicates deps)))))
138
a54c94a4
LC
139(define (manifest->gexp manifest)
140 "Return a representation of MANIFEST as a gexp."
141 (define (entry->gexp entry)
cc4ecc2d 142 (match entry
a54c94a4
LC
143 (($ <manifest-entry> name version output (? string? path) (deps ...))
144 #~(#$name #$version #$output #$path #$deps))
145 (($ <manifest-entry> name version output (? package? package) (deps ...))
146 #~(#$name #$version #$output
147 (ungexp package (or output "out")) #$deps))))
cc4ecc2d
LC
148
149 (match manifest
150 (($ <manifest> (entries ...))
a54c94a4
LC
151 #~(manifest (version 1)
152 (packages #$(map entry->gexp entries))))))
cc4ecc2d
LC
153
154(define (sexp->manifest sexp)
155 "Parse SEXP as a manifest."
156 (match sexp
157 (('manifest ('version 0)
158 ('packages ((name version output path) ...)))
159 (manifest
160 (map (lambda (name version output path)
161 (manifest-entry
162 (name name)
163 (version version)
164 (output output)
a54c94a4 165 (item path)))
cc4ecc2d
LC
166 name version output path)))
167
168 ;; Version 1 adds a list of propagated inputs to the
169 ;; name/version/output/path tuples.
170 (('manifest ('version 1)
171 ('packages ((name version output path deps) ...)))
172 (manifest
173 (map (lambda (name version output path deps)
d34736c5
LC
174 ;; Up to Guix 0.7 included, dependencies were listed as ("gmp"
175 ;; "/gnu/store/...-gmp") for instance. Discard the 'label' in
176 ;; such lists.
177 (let ((deps (match deps
178 (((labels directories) ...)
179 directories)
180 ((directories ...)
181 directories))))
182 (manifest-entry
183 (name name)
184 (version version)
185 (output output)
186 (item path)
187 (dependencies deps))))
cc4ecc2d
LC
188 name version output path deps)))
189
190 (_
191 (error "unsupported manifest format" manifest))))
192
193(define (read-manifest port)
194 "Return the packages listed in MANIFEST."
195 (sexp->manifest (read port)))
196
a2078770
LC
197(define (entry-predicate pattern)
198 "Return a procedure that returns #t when passed a manifest entry that
199matches NAME/OUTPUT/VERSION. OUTPUT and VERSION may be #f, in which case they
200are ignored."
201 (match pattern
202 (($ <manifest-pattern> name version output)
203 (match-lambda
204 (($ <manifest-entry> entry-name entry-version entry-output)
205 (and (string=? entry-name name)
206 (or (not entry-output) (not output)
207 (string=? entry-output output))
208 (or (not version)
209 (string=? entry-version version))))))))
210
211(define (manifest-remove manifest patterns)
212 "Remove entries for each of PATTERNS from MANIFEST. Each item in PATTERNS
213must be a manifest-pattern."
214 (define (remove-entry pattern lst)
215 (remove (entry-predicate pattern) lst))
216
217 (make-manifest (fold remove-entry
cc4ecc2d 218 (manifest-entries manifest)
a2078770 219 patterns)))
cc4ecc2d 220
f7554030
AK
221(define (manifest-add manifest entries)
222 "Add a list of manifest ENTRIES to MANIFEST and return new manifest.
223Remove MANIFEST entries that have the same name and output as ENTRIES."
224 (define (same-entry? entry name output)
225 (match entry
226 (($ <manifest-entry> entry-name _ entry-output _ ...)
227 (and (equal? name entry-name)
228 (equal? output entry-output)))))
229
230 (make-manifest
231 (append entries
232 (fold (lambda (entry result)
233 (match entry
234 (($ <manifest-entry> name _ out _ ...)
235 (filter (negate (cut same-entry? <> name out))
236 result))))
237 (manifest-entries manifest)
238 entries))))
239
a2078770
LC
240(define (manifest-installed? manifest pattern)
241 "Return #t if MANIFEST has an entry matching PATTERN (a manifest-pattern),
242#f otherwise."
243 (->bool (find (entry-predicate pattern)
cc4ecc2d
LC
244 (manifest-entries manifest))))
245
a2078770
LC
246(define (manifest-matching-entries manifest patterns)
247 "Return all the entries of MANIFEST that match one of the PATTERNS."
248 (define predicates
249 (map entry-predicate patterns))
250
251 (define (matches? entry)
252 (any (lambda (pred)
253 (pred entry))
254 predicates))
255
256 (filter matches? (manifest-entries manifest)))
257
cc4ecc2d 258\f
343745c8
AK
259;;;
260;;; Manifest transactions.
261;;;
262
263(define-record-type* <manifest-transaction> manifest-transaction
264 make-manifest-transaction
265 manifest-transaction?
266 (install manifest-transaction-install ; list of <manifest-entry>
267 (default '()))
268 (remove manifest-transaction-remove ; list of <manifest-pattern>
269 (default '())))
270
79601521
LC
271(define (manifest-transaction-effects manifest transaction)
272 "Compute the effect of applying TRANSACTION to MANIFEST. Return 3 values:
273the list of packages that would be removed, installed, or upgraded when
274applying TRANSACTION to MANIFEST."
275 (define (manifest-entry->pattern entry)
276 (manifest-pattern
277 (name (manifest-entry-name entry))
278 (output (manifest-entry-output entry))))
279
280 (let loop ((input (manifest-transaction-install transaction))
281 (install '())
282 (upgrade '()))
283 (match input
284 (()
285 (let ((remove (manifest-transaction-remove transaction)))
286 (values (manifest-matching-entries manifest remove)
287 (reverse install) (reverse upgrade))))
288 ((entry rest ...)
289 ;; Check whether installing ENTRY corresponds to the installation of a
290 ;; new package or to an upgrade.
291
292 ;; XXX: When the exact same output directory is installed, we're not
293 ;; really upgrading anything. Add a check for that case.
294 (let* ((pattern (manifest-entry->pattern entry))
295 (upgrade? (manifest-installed? manifest pattern)))
296 (loop rest
297 (if upgrade? install (cons entry install))
298 (if upgrade? (cons entry upgrade) upgrade)))))))
299
343745c8
AK
300(define (manifest-perform-transaction manifest transaction)
301 "Perform TRANSACTION on MANIFEST and return new manifest."
302 (let ((install (manifest-transaction-install transaction))
303 (remove (manifest-transaction-remove transaction)))
304 (manifest-add (manifest-remove manifest remove)
305 install)))
306
307(define* (manifest-show-transaction store manifest transaction
308 #:key dry-run?)
309 "Display what will/would be installed/removed from MANIFEST by TRANSACTION."
6b74bb0a
AK
310 (define (package-strings name version output item)
311 (map (lambda (name version output item)
312 (format #f " ~a-~a\t~a\t~a" name version output
313 (if (package? item)
314 (package-output store item output)
315 item)))
316 name version output item))
317
79601521
LC
318 (let-values (((remove install upgrade)
319 (manifest-transaction-effects manifest transaction)))
343745c8 320 (match remove
6b74bb0a 321 ((($ <manifest-entry> name version output item _) ..1)
343745c8 322 (let ((len (length name))
6b74bb0a 323 (remove (package-strings name version output item)))
343745c8
AK
324 (if dry-run?
325 (format (current-error-port)
326 (N_ "The following package would be removed:~%~{~a~%~}~%"
327 "The following packages would be removed:~%~{~a~%~}~%"
328 len)
329 remove)
330 (format (current-error-port)
331 (N_ "The following package will be removed:~%~{~a~%~}~%"
332 "The following packages will be removed:~%~{~a~%~}~%"
333 len)
334 remove))))
335 (_ #f))
6b74bb0a
AK
336 (match upgrade
337 ((($ <manifest-entry> name version output item _) ..1)
338 (let ((len (length name))
339 (upgrade (package-strings name version output item)))
340 (if dry-run?
341 (format (current-error-port)
342 (N_ "The following package would be upgraded:~%~{~a~%~}~%"
343 "The following packages would be upgraded:~%~{~a~%~}~%"
344 len)
345 upgrade)
346 (format (current-error-port)
347 (N_ "The following package will be upgraded:~%~{~a~%~}~%"
348 "The following packages will be upgraded:~%~{~a~%~}~%"
349 len)
350 upgrade))))
351 (_ #f))
343745c8
AK
352 (match install
353 ((($ <manifest-entry> name version output item _) ..1)
354 (let ((len (length name))
6b74bb0a 355 (install (package-strings name version output item)))
343745c8
AK
356 (if dry-run?
357 (format (current-error-port)
358 (N_ "The following package would be installed:~%~{~a~%~}~%"
359 "The following packages would be installed:~%~{~a~%~}~%"
360 len)
361 install)
362 (format (current-error-port)
363 (N_ "The following package will be installed:~%~{~a~%~}~%"
364 "The following packages will be installed:~%~{~a~%~}~%"
365 len)
366 install))))
367 (_ #f))))
368
369\f
cc4ecc2d
LC
370;;;
371;;; Profiles.
372;;;
373
79ee406d
LC
374(define (manifest-inputs manifest)
375 "Return the list of inputs for MANIFEST. Each input has one of the
376following forms:
377
378 (PACKAGE OUTPUT-NAME)
379
380or
381
382 STORE-PATH
383"
384 (append-map (match-lambda
385 (($ <manifest-entry> name version
386 output (? package? package) deps)
387 `((,package ,output) ,@deps))
388 (($ <manifest-entry> name version output path deps)
389 ;; Assume PATH and DEPS are already valid.
390 `(,path ,@deps)))
391 (manifest-entries manifest)))
392
393(define (info-dir-file manifest)
394 "Return a derivation that builds the 'dir' file for all the entries of
395MANIFEST."
2f0556ae
LC
396 (define texinfo ;lazy reference
397 (module-ref (resolve-interface '(gnu packages texinfo)) 'texinfo))
398 (define gzip ;lazy reference
399 (module-ref (resolve-interface '(gnu packages compression)) 'gzip))
400
79ee406d 401 (define build
a54c94a4 402 #~(begin
79ee406d
LC
403 (use-modules (guix build utils)
404 (srfi srfi-1) (srfi srfi-26)
405 (ice-9 ftw))
406
407 (define (info-file? file)
408 (or (string-suffix? ".info" file)
409 (string-suffix? ".info.gz" file)))
410
411 (define (info-files top)
412 (let ((infodir (string-append top "/share/info")))
413 (map (cut string-append infodir "/" <>)
c2815c0f 414 (or (scandir infodir info-file?) '()))))
79ee406d
LC
415
416 (define (install-info info)
2f0556ae 417 (setenv "PATH" (string-append #+gzip "/bin")) ;for info.gz files
79ee406d
LC
418 (zero?
419 (system* (string-append #+texinfo "/bin/install-info")
420 info (string-append #$output "/share/info/dir"))))
421
422 (mkdir-p (string-append #$output "/share/info"))
423 (every install-info
424 (append-map info-files
425 '#$(manifest-inputs manifest)))))
426
427 ;; Don't depend on Texinfo when there's nothing to do.
428 (if (null? (manifest-entries manifest))
429 (gexp->derivation "info-dir" #~(mkdir #$output))
430 (gexp->derivation "info-dir" build
431 #:modules '((guix build utils)))))
432
433(define* (profile-derivation manifest #:key (info-dir? #t))
434 "Return a derivation that builds a profile (aka. 'user environment') with
435the given MANIFEST. The profile includes a top-level Info 'dir' file, unless
436INFO-DIR? is #f."
437 (mlet %store-monad ((info-dir (if info-dir?
438 (info-dir-file manifest)
439 (return #f))))
440 (define inputs
441 (if info-dir
442 (cons info-dir (manifest-inputs manifest))
443 (manifest-inputs manifest)))
444
445 (define builder
446 #~(begin
447 (use-modules (ice-9 pretty-print)
448 (guix build union))
449
450 (setvbuf (current-output-port) _IOLBF)
451 (setvbuf (current-error-port) _IOLBF)
452
453 (union-build #$output '#$inputs
454 #:log-port (%make-void-port "w"))
455 (call-with-output-file (string-append #$output "/manifest")
456 (lambda (p)
457 (pretty-print '#$(manifest->gexp manifest) p)))))
458
459 (gexp->derivation "profile" builder
460 #:modules '((guix build union))
461 #:local-build? #t)))
cc4ecc2d
LC
462
463(define (profile-regexp profile)
464 "Return a regular expression that matches PROFILE's name and number."
465 (make-regexp (string-append "^" (regexp-quote (basename profile))
466 "-([0-9]+)")))
467
468(define (generation-number profile)
469 "Return PROFILE's number or 0. An absolute file name must be used."
470 (or (and=> (false-if-exception (regexp-exec (profile-regexp profile)
471 (basename (readlink profile))))
472 (compose string->number (cut match:substring <> 1)))
473 0))
474
475(define (generation-numbers profile)
476 "Return the sorted list of generation numbers of PROFILE, or '(0) if no
477former profiles were found."
478 (define* (scandir name #:optional (select? (const #t))
479 (entry<? (@ (ice-9 i18n) string-locale<?)))
480 ;; XXX: Bug-fix version introduced in Guile v2.0.6-62-g139ce19.
481 (define (enter? dir stat result)
482 (and stat (string=? dir name)))
483
484 (define (visit basename result)
485 (if (select? basename)
486 (cons basename result)
487 result))
488
489 (define (leaf name stat result)
490 (and result
491 (visit (basename name) result)))
492
493 (define (down name stat result)
494 (visit "." '()))
495
496 (define (up name stat result)
497 (visit ".." result))
498
499 (define (skip name stat result)
500 ;; All the sub-directories are skipped.
501 (visit (basename name) result))
502
503 (define (error name* stat errno result)
504 (if (string=? name name*) ; top-level NAME is unreadable
505 result
506 (visit (basename name*) result)))
507
508 (and=> (file-system-fold enter? leaf down up skip error #f name lstat)
509 (lambda (files)
510 (sort files entry<?))))
511
512 (match (scandir (dirname profile)
513 (cute regexp-exec (profile-regexp profile) <>))
514 (#f ; no profile directory
515 '(0))
516 (() ; no profiles
517 '(0))
518 ((profiles ...) ; former profiles around
519 (sort (map (compose string->number
520 (cut match:substring <> 1)
521 (cute regexp-exec (profile-regexp profile) <>))
522 profiles)
523 <))))
524
525(define (previous-generation-number profile number)
526 "Return the number of the generation before generation NUMBER of
527PROFILE, or 0 if none exists. It could be NUMBER - 1, but it's not the
528case when generations have been deleted (there are \"holes\")."
529 (fold (lambda (candidate highest)
530 (if (and (< candidate number) (> candidate highest))
531 candidate
532 highest))
533 0
534 (generation-numbers profile)))
535
536(define (generation-file-name profile generation)
537 "Return the file name for PROFILE's GENERATION."
538 (format #f "~a-~a-link" profile generation))
539
540(define (generation-time profile number)
541 "Return the creation time of a generation in the UTC format."
542 (make-time time-utc 0
543 (stat:ctime (stat (generation-file-name profile number)))))
544
545;;; profiles.scm ends here