emacs: Improve minibuffer messages.
[jackhill/guix/guix.git] / emacs / guix-main.scm
CommitLineData
457f60fa
AK
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2014 Alex Kost <alezost@gmail.com>
3;;;
4;;; This file is part of GNU Guix.
5;;;
6;;; GNU Guix is free software; you can redistribute it and/or modify it
7;;; under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 3 of the License, or (at
9;;; your option) any later version.
10;;;
11;;; GNU Guix is distributed in the hope that it will be useful, but
12;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19;;; Commentary:
20
21;; Information about packages and generations is passed to the elisp
22;; side in the form of alists of parameters (such as ‘name’ or
81b339fe 23;; ‘version’) and their values.
457f60fa 24
81b339fe 25;; ‘entries’ procedure is the “entry point” for the elisp side to get
457f60fa
AK
26;; information about packages and generations.
27
28;; Since name/version pair is not necessarily unique, we use
29;; `object-address' to identify a package (for ‘id’ parameter), if
30;; possible. However for the obsolete packages (that can be found in
31;; installed manifest but not in a package directory), ‘id’ parameter is
32;; still "name-version" string. So ‘id’ package parameter in the code
33;; below is either an object-address number or a full-name string.
457f60fa
AK
34
35;; To speed-up the process of getting information, the following
36;; auxiliary variables are used:
37;;
38;; - `%packages' - VHash of "package address"/"package" pairs.
39;;
40;; - `%package-table' - Hash table of
41;; "name+version key"/"list of packages" pairs.
457f60fa
AK
42
43;;; Code:
44
45(use-modules
46 (ice-9 vlist)
47 (ice-9 match)
48 (srfi srfi-1)
49 (srfi srfi-11)
50 (srfi srfi-19)
51 (srfi srfi-26)
52 (guix)
53 (guix packages)
54 (guix profiles)
55 (guix licenses)
56 (guix utils)
57 (guix ui)
58 (guix scripts package)
2d7bf949 59 (guix scripts pull)
457f60fa
AK
60 (gnu packages))
61
62(define-syntax-rule (first-or-false lst)
63 (and (not (null? lst))
64 (first lst)))
65
81b339fe
AK
66(define (list-maybe obj)
67 (if (list? obj) obj (list obj)))
68
457f60fa
AK
69(define full-name->name+version package-name->name+version)
70(define (name+version->full-name name version)
71 (string-append name "-" version))
72
73(define* (make-package-specification name #:optional version output)
74 (let ((full-name (if version
75 (name+version->full-name name version)
76 name)))
77 (if output
78 (string-append full-name ":" output)
79 full-name)))
80
81(define name+version->key cons)
82(define key->name+version car+cdr)
83
457f60fa
AK
84(define %packages
85 (fold-packages (lambda (pkg res)
86 (vhash-consq (object-address pkg) pkg res))
87 vlist-null))
88
89(define %package-table
90 (let ((table (make-hash-table (vlist-length %packages))))
91 (vlist-for-each
92 (lambda (elem)
93 (match elem
94 ((address . pkg)
95 (let* ((key (name+version->key (package-name pkg)
96 (package-version pkg)))
97 (ref (hash-ref table key)))
98 (hash-set! table key
99 (if ref (cons pkg ref) (list pkg)))))))
100 %packages)
101 table))
102
81b339fe
AK
103(define (manifest-entry->name+version+output entry)
104 (values
105 (manifest-entry-name entry)
106 (manifest-entry-version entry)
107 (manifest-entry-output entry)))
108
109(define (manifest-entries->hash-table entries)
110 "Return a hash table of name keys and lists of matching manifest ENTRIES."
111 (let ((table (make-hash-table (length entries))))
112 (for-each (lambda (entry)
113 (let* ((key (manifest-entry-name entry))
114 (ref (hash-ref table key)))
115 (hash-set! table key
116 (if ref (cons entry ref) (list entry)))))
117 entries)
118 table))
457f60fa 119
81b339fe
AK
120(define (manifest=? m1 m2)
121 (or (eq? m1 m2)
122 (equal? m1 m2)))
123
124(define manifest->hash-table
125 (let ((current-manifest #f)
126 (current-table #f))
127 (lambda (manifest)
128 "Return a hash table of name keys and matching MANIFEST entries."
129 (unless (manifest=? manifest current-manifest)
130 (set! current-manifest manifest)
131 (set! current-table (manifest-entries->hash-table
132 (manifest-entries manifest))))
133 current-table)))
134
135(define* (manifest-entries-by-name manifest name #:optional version output)
136 "Return a list of MANIFEST entries matching NAME, VERSION and OUTPUT."
137 (let ((entries (or (hash-ref (manifest->hash-table manifest) name)
138 '())))
139 (if (or version output)
140 (filter (lambda (entry)
141 (and (or (not version)
142 (equal? version (manifest-entry-version entry)))
143 (or (not output)
144 (equal? output (manifest-entry-output entry)))))
145 entries)
146 entries)))
147
148(define (manifest-entry-by-output entries output)
149 "Return a manifest entry from ENTRIES matching OUTPUT."
150 (find (lambda (entry)
151 (string= output (manifest-entry-output entry)))
152 entries))
153
154(define (fold-manifest-by-name manifest proc init)
155 "Fold over MANIFEST entries.
156Call (PROC NAME VERSION ENTRIES RESULT), using INIT as the initial value
157of RESULT. ENTRIES is a list of manifest entries with NAME/VERSION."
158 (hash-fold (lambda (name entries res)
159 (proc name (manifest-entry-version (car entries))
160 entries res))
457f60fa 161 init
81b339fe 162 (manifest->hash-table manifest)))
457f60fa
AK
163
164(define* (object-transformer param-alist #:optional (params '()))
81b339fe 165 "Return procedure transforming objects into alist of parameter/value pairs.
457f60fa 166
81b339fe
AK
167PARAM-ALIST is alist of available parameters (symbols) and procedures
168returning values of these parameters. Each procedure is applied to
169objects.
457f60fa 170
81b339fe
AK
171PARAMS is list of parameters from PARAM-ALIST that should be returned by
172a resulting procedure. If PARAMS is not specified or is an empty list,
173use all available parameters.
457f60fa
AK
174
175Example:
176
81b339fe
AK
177 (let* ((alist `((plus1 . ,1+) (minus1 . ,1-) (mul2 . ,(cut * 2 <>))))
178 (number->alist (object-transformer alist '(plus1 mul2))))
457f60fa
AK
179 (number->alist 8))
180 =>
181 ((plus1 . 9) (mul2 . 16))
182"
81b339fe
AK
183 (let* ((use-all-params (null? params))
184 (alist (filter-map (match-lambda
185 ((param . proc)
186 (and (or use-all-params
187 (memq param params))
188 (cons param proc)))
189 (_ #f))
190 param-alist)))
191 (lambda objects
457f60fa 192 (map (match-lambda
81b339fe
AK
193 ((param . proc)
194 (cons param (apply proc objects))))
457f60fa
AK
195 alist))))
196
81b339fe
AK
197(define %manifest-entry-param-alist
198 `((output . ,manifest-entry-output)
199 (path . ,manifest-entry-item)
200 (dependencies . ,manifest-entry-dependencies)))
457f60fa 201
81b339fe
AK
202(define manifest-entry->sexp
203 (object-transformer %manifest-entry-param-alist))
457f60fa 204
81b339fe
AK
205(define (manifest-entries->sexps entries)
206 (map manifest-entry->sexp entries))
457f60fa
AK
207
208(define (package-inputs-names inputs)
81b339fe 209 "Return a list of full names of the packages from package INPUTS."
457f60fa
AK
210 (filter-map (match-lambda
211 ((_ (? package? package))
212 (package-full-name package))
213 (_ #f))
214 inputs))
215
216(define (package-license-names package)
81b339fe
AK
217 "Return a list of license names of the PACKAGE."
218 (filter-map (lambda (license)
219 (and (license? license)
220 (license-name license)))
221 (list-maybe (package-license package))))
457f60fa
AK
222
223(define (package-unique? package)
224 "Return #t if PACKAGE is a single package with such name/version."
81b339fe
AK
225 (null? (cdr (packages-by-name (package-name package)
226 (package-version package)))))
227
228(define %package-param-alist
229 `((id . ,object-address)
230 (package-id . ,object-address)
231 (name . ,package-name)
232 (version . ,package-version)
233 (license . ,package-license-names)
234 (synopsis . ,package-synopsis)
235 (description . ,package-description)
236 (home-url . ,package-home-page)
237 (outputs . ,package-outputs)
238 (non-unique . ,(negate package-unique?))
239 (inputs . ,(lambda (pkg)
240 (package-inputs-names
241 (package-inputs pkg))))
242 (native-inputs . ,(lambda (pkg)
243 (package-inputs-names
244 (package-native-inputs pkg))))
245 (propagated-inputs . ,(lambda (pkg)
246 (package-inputs-names
247 (package-propagated-inputs pkg))))
248 (location . ,(lambda (pkg)
249 (location->string (package-location pkg))))))
457f60fa
AK
250
251(define (package-param package param)
81b339fe
AK
252 "Return a value of a PACKAGE PARAM."
253 (and=> (assq-ref %package-param-alist param)
457f60fa
AK
254 (cut <> package)))
255
81b339fe
AK
256\f
257;;; Finding packages.
258
259(define (package-by-address address)
260 (and=> (vhash-assq address %packages)
261 cdr))
262
263(define (packages-by-name+version name version)
264 (or (hash-ref %package-table
265 (name+version->key name version))
266 '()))
267
268(define (packages-by-full-name full-name)
269 (call-with-values
270 (lambda () (full-name->name+version full-name))
271 packages-by-name+version))
272
273(define (packages-by-id id)
274 (if (integer? id)
275 (let ((pkg (package-by-address id)))
276 (if pkg (list pkg) '()))
277 (packages-by-full-name id)))
278
279(define (id->name+version id)
280 (if (integer? id)
281 (and=> (package-by-address id)
282 (lambda (pkg)
283 (values (package-name pkg)
284 (package-version pkg))))
285 (full-name->name+version id)))
286
287(define (package-by-id id)
288 (first-or-false (packages-by-id id)))
289
290(define (newest-package-by-id id)
291 (and=> (id->name+version id)
292 (lambda (name)
293 (first-or-false (find-best-packages-by-name name #f)))))
294
295(define (matching-packages predicate)
457f60fa
AK
296 (fold-packages (lambda (pkg res)
297 (if (predicate pkg)
81b339fe 298 (cons pkg res)
457f60fa
AK
299 res))
300 '()))
301
81b339fe
AK
302(define (filter-packages-by-output packages output)
303 (filter (lambda (package)
304 (member output (package-outputs package)))
305 packages))
306
307(define* (packages-by-name name #:optional version output)
308 "Return a list of packages matching NAME, VERSION and OUTPUT."
309 (let ((packages (if version
310 (packages-by-name+version name version)
311 (matching-packages
312 (lambda (pkg) (string=? name (package-name pkg)))))))
313 (if output
314 (filter-packages-by-output packages output)
315 packages)))
457f60fa 316
81b339fe
AK
317(define (manifest-entry->packages entry)
318 (call-with-values
319 (lambda () (manifest-entry->name+version+output entry))
320 packages-by-name))
457f60fa 321
81b339fe
AK
322(define (packages-by-regexp regexp match-params)
323 "Return a list of packages matching REGEXP string.
457f60fa
AK
324MATCH-PARAMS is a list of parameters that REGEXP can match."
325 (define (package-match? package regexp)
326 (any (lambda (param)
327 (let ((val (package-param package param)))
328 (and (string? val) (regexp-exec regexp val))))
329 match-params))
330
457f60fa 331 (let ((re (make-regexp regexp regexp/icase)))
81b339fe
AK
332 (matching-packages (cut package-match? <> re))))
333
334(define (all-available-packages)
335 "Return a list of all available packages."
336 (matching-packages (const #t)))
337
338(define (newest-available-packages)
339 "Return a list of the newest available packages."
457f60fa
AK
340 (vhash-fold (lambda (name elem res)
341 (match elem
81b339fe
AK
342 ((_ newest pkgs ...)
343 (cons newest res))))
457f60fa
AK
344 '()
345 (find-newest-available-packages)))
346
81b339fe
AK
347\f
348;;; Making package/output patterns.
349
350(define (specification->package-pattern specification)
351 (call-with-values
352 (lambda ()
353 (full-name->name+version specification))
354 list))
457f60fa 355
81b339fe
AK
356(define (specification->output-pattern specification)
357 (call-with-values
358 (lambda ()
359 (package-specification->name+version+output specification #f))
360 list))
457f60fa 361
81b339fe
AK
362(define (id->package-pattern id)
363 (if (integer? id)
364 (package-by-address id)
365 (specification->package-pattern id)))
366
367(define (id->output-pattern id)
368 "Return an output pattern by output ID.
369ID should be '<package-address>:<output>' or '<name>-<version>:<output>'."
370 (let-values (((name version output)
371 (package-specification->name+version+output id)))
372 (if version
373 (list name version output)
374 (list (package-by-address (string->number name))
375 output))))
376
377(define (specifications->package-patterns . specifications)
378 (map specification->package-pattern specifications))
379
380(define (specifications->output-patterns . specifications)
381 (map specification->output-pattern specifications))
382
383(define (ids->package-patterns . ids)
384 (map id->package-pattern ids))
385
386(define (ids->output-patterns . ids)
387 (map id->output-pattern ids))
388
389(define* (manifest-patterns-result packages res obsolete-pattern
390 #:optional installed-pattern)
391 "Auxiliary procedure for 'manifest-package-patterns' and
392'manifest-output-patterns'."
393 (if (null? packages)
394 (cons (obsolete-pattern) res)
395 (if installed-pattern
396 ;; We don't need duplicates for a list of installed packages,
397 ;; so just take any (car) package.
398 (cons (installed-pattern (car packages)) res)
399 res)))
400
401(define* (manifest-package-patterns manifest #:optional obsolete-only?)
402 "Return a list of package patterns for MANIFEST entries.
403If OBSOLETE-ONLY? is #f, use all entries, otherwise make patterns only
404for obsolete packages."
405 (fold-manifest-by-name
406 manifest
457f60fa 407 (lambda (name version entries res)
81b339fe
AK
408 (manifest-patterns-result (packages-by-name name version)
409 res
410 (lambda () (list name version entries))
411 (and (not obsolete-only?)
412 (cut list <> entries))))
457f60fa
AK
413 '()))
414
81b339fe
AK
415(define* (manifest-output-patterns manifest #:optional obsolete-only?)
416 "Return a list of output patterns for MANIFEST entries.
417If OBSOLETE-ONLY? is #f, use all entries, otherwise make patterns only
418for obsolete packages."
419 (fold (lambda (entry res)
420 (manifest-patterns-result (manifest-entry->packages entry)
421 res
422 (lambda () entry)
423 (and (not obsolete-only?)
424 (cut list <> entry))))
425 '()
426 (manifest-entries manifest)))
427
428(define (obsolete-package-patterns manifest)
429 (manifest-package-patterns manifest #t))
430
431(define (obsolete-output-patterns manifest)
432 (manifest-output-patterns manifest #t))
433
457f60fa 434\f
81b339fe 435;;; Transforming package/output patterns into alists.
457f60fa 436
81b339fe
AK
437(define (obsolete-package-sexp name version entries)
438 "Return an alist with information about obsolete package.
439ENTRIES is a list of installed manifest entries."
440 `((id . ,(name+version->full-name name version))
441 (name . ,name)
442 (version . ,version)
443 (outputs . ,(map manifest-entry-output entries))
444 (obsolete . #t)
445 (installed . ,(manifest-entries->sexps entries))))
446
447(define (package-pattern-transformer manifest params)
448 "Return 'package-pattern->package-sexps' procedure."
449 (define package->sexp
450 (object-transformer %package-param-alist params))
451
452 (define* (sexp-by-package package #:optional
453 (entries (manifest-entries-by-name
454 manifest
455 (package-name package)
456 (package-version package))))
457 (cons (cons 'installed (manifest-entries->sexps entries))
458 (package->sexp package)))
459
460 (define (->sexps pattern)
461 (match pattern
462 ((? package? package)
463 (list (sexp-by-package package)))
464 (((? package? package) entries)
465 (list (sexp-by-package package entries)))
466 ((name version entries)
467 (list (obsolete-package-sexp
468 name version entries)))
469 ((name version)
470 (let ((packages (packages-by-name name version)))
471 (if (null? packages)
472 (let ((entries (manifest-entries-by-name
473 manifest name version)))
474 (if (null? entries)
475 '()
476 (list (obsolete-package-sexp
477 name version entries))))
ce8b2953
AK
478 (map sexp-by-package packages))))
479 (_ '())))
81b339fe
AK
480
481 ->sexps)
482
483(define (output-pattern-transformer manifest params)
484 "Return 'output-pattern->output-sexps' procedure."
485 (define package->sexp
486 (object-transformer (alist-delete 'id %package-param-alist)
487 params))
488
489 (define manifest-entry->sexp
490 (object-transformer (alist-delete 'output %manifest-entry-param-alist)
491 params))
492
493 (define* (output-sexp pkg-alist pkg-address output
494 #:optional entry)
495 (let ((entry-alist (if entry
496 (manifest-entry->sexp entry)
497 '()))
498 (base `((id . ,(string-append
499 (number->string pkg-address)
500 ":" output))
501 (output . ,output)
502 (installed . ,(->bool entry)))))
503 (append entry-alist base pkg-alist)))
504
505 (define (obsolete-output-sexp entry)
506 (let-values (((name version output)
507 (manifest-entry->name+version+output entry)))
508 (let ((base `((id . ,(make-package-specification
509 name version output))
510 (package-id . ,(name+version->full-name name version))
511 (name . ,name)
512 (version . ,version)
513 (output . ,output)
514 (obsolete . #t)
515 (installed . #t))))
516 (append (manifest-entry->sexp entry) base))))
517
518 (define* (sexps-by-package package #:optional output
519 (entries (manifest-entries-by-name
520 manifest
521 (package-name package)
522 (package-version package))))
523 ;; Assuming that PACKAGE has this OUTPUT.
524 (let ((pkg-alist (package->sexp package))
525 (address (object-address package))
526 (outputs (if output
527 (list output)
528 (package-outputs package))))
529 (map (lambda (output)
530 (output-sexp pkg-alist address output
531 (manifest-entry-by-output entries output)))
532 outputs)))
533
534 (define* (sexps-by-manifest-entry entry #:optional
535 (packages (manifest-entry->packages
536 entry)))
537 (if (null? packages)
538 (list (obsolete-output-sexp entry))
539 (map (lambda (package)
540 (output-sexp (package->sexp package)
541 (object-address package)
542 (manifest-entry-output entry)
543 entry))
544 packages)))
545
546 (define (->sexps pattern)
547 (match pattern
548 ((? package? package)
549 (sexps-by-package package))
550 ((package (? string? output))
551 (sexps-by-package package output))
552 ((? manifest-entry? entry)
553 (list (obsolete-output-sexp entry)))
554 ((package entry)
555 (sexps-by-manifest-entry entry (list package)))
556 ((name version output)
557 (let ((packages (packages-by-name name version output)))
558 (if (null? packages)
559 (let ((entries (manifest-entries-by-name
560 manifest name version output)))
561 (append-map (cut sexps-by-manifest-entry <>)
562 entries))
563 (append-map (cut sexps-by-package <> output)
ce8b2953
AK
564 packages))))
565 (_ '())))
81b339fe
AK
566
567 ->sexps)
568
569(define (entry-type-error entry-type)
570 (error (format #f "Wrong entry-type '~a'" entry-type)))
571
572(define (search-type-error entry-type search-type)
573 (error (format #f "Wrong search type '~a' for entry-type '~a'"
574 search-type entry-type)))
575
576(define %pattern-transformers
577 `((package . ,package-pattern-transformer)
578 (output . ,output-pattern-transformer)))
579
580(define (pattern-transformer entry-type)
581 (assq-ref %pattern-transformers entry-type))
582
583;; All procedures from inner alists are called with (MANIFEST . SEARCH-VALS)
584;; as arguments; see `package/output-sexps'.
585(define %patterns-makers
586 (let* ((apply-to-rest (lambda (proc)
587 (lambda (_ . rest) (apply proc rest))))
588 (apply-to-first (lambda (proc)
589 (lambda (first . _) (proc first))))
590 (manifest-package-proc (apply-to-first manifest-package-patterns))
591 (manifest-output-proc (apply-to-first manifest-output-patterns))
592 (regexp-proc (lambda (_ regexp params . __)
593 (packages-by-regexp regexp params)))
594 (all-proc (lambda _ (all-available-packages)))
595 (newest-proc (lambda _ (newest-available-packages))))
596 `((package
597 (id . ,(apply-to-rest ids->package-patterns))
598 (name . ,(apply-to-rest specifications->package-patterns))
599 (installed . ,manifest-package-proc)
600 (generation . ,manifest-package-proc)
601 (obsolete . ,(apply-to-first obsolete-package-patterns))
602 (regexp . ,regexp-proc)
603 (all-available . ,all-proc)
604 (newest-available . ,newest-proc))
605 (output
606 (id . ,(apply-to-rest ids->output-patterns))
607 (name . ,(apply-to-rest specifications->output-patterns))
608 (installed . ,manifest-output-proc)
609 (generation . ,manifest-output-proc)
610 (obsolete . ,(apply-to-first obsolete-output-patterns))
611 (regexp . ,regexp-proc)
612 (all-available . ,all-proc)
613 (newest-available . ,newest-proc)))))
614
615(define (patterns-maker entry-type search-type)
616 (or (and=> (assq-ref %patterns-makers entry-type)
617 (cut assq-ref <> search-type))
618 (search-type-error entry-type search-type)))
619
620(define (package/output-sexps profile params entry-type
621 search-type search-vals)
622 "Return information about packages or package outputs.
623See 'entry-sexps' for details."
624 (let* ((profile (if (eq? search-type 'generation)
625 (generation-file-name profile (car search-vals))
626 profile))
627 (manifest (profile-manifest profile))
628 (patterns (apply (patterns-maker entry-type search-type)
629 manifest search-vals))
630 (->sexps ((pattern-transformer entry-type) manifest params)))
631 (append-map ->sexps patterns)))
632
633\f
634;;; Getting information about generations.
457f60fa
AK
635
636(define (generation-param-alist profile)
81b339fe 637 "Return an alist of generation parameters and procedures for PROFILE."
c2379b3c
AK
638 (let ((current (generation-number profile)))
639 `((id . ,identity)
640 (number . ,identity)
641 (prev-number . ,(cut previous-generation-number profile <>))
642 (current . ,(cut = current <>))
643 (path . ,(cut generation-file-name profile <>))
644 (time . ,(lambda (gen)
645 (time-second (generation-time profile gen)))))))
457f60fa 646
81b339fe
AK
647(define (matching-generations profile predicate)
648 "Return a list of PROFILE generations matching PREDICATE."
649 (filter predicate (profile-generations profile)))
457f60fa 650
81b339fe
AK
651(define (last-generations profile number)
652 "Return a list of last NUMBER generations.
653If NUMBER is 0 or less, return all generations."
457f60fa
AK
654 (let ((generations (profile-generations profile))
655 (number (if (<= number 0) +inf.0 number)))
81b339fe
AK
656 (if (> (length generations) number)
657 (list-head (reverse generations) number)
658 generations)))
457f60fa 659
81b339fe
AK
660(define (find-generations profile search-type search-vals)
661 "Find PROFILE's generations matching SEARCH-TYPE and SEARCH-VALS."
662 (case search-type
663 ((id)
ce8b2953 664 (matching-generations profile (cut memq <> search-vals)))
81b339fe
AK
665 ((last)
666 (last-generations profile (car search-vals)))
667 ((all)
668 (last-generations profile +inf.0))
189cea27
AK
669 ((time)
670 (match search-vals
671 ((from to)
672 (matching-generations
673 profile
674 (lambda (gen)
675 (let ((time (time-second (generation-time profile gen))))
676 (< from time to)))))
677 (_ '())))
81b339fe
AK
678 (else (search-type-error "generation" search-type))))
679
680(define (generation-sexps profile params search-type search-vals)
681 "Return information about generations.
682See 'entry-sexps' for details."
683 (let ((generations (find-generations profile search-type search-vals))
684 (->sexp (object-transformer (generation-param-alist profile)
685 params)))
686 (map ->sexp generations)))
457f60fa
AK
687
688\f
81b339fe
AK
689;;; Getting package/output/generation entries (alists).
690
691(define (entries profile params entry-type search-type search-vals)
692 "Return information about entries.
693
694ENTRY-TYPE is a symbol defining a type of returning information. Should
695be: 'package', 'output' or 'generation'.
696
697SEARCH-TYPE and SEARCH-VALS define how to get the information.
698SEARCH-TYPE should be one of the following symbols:
699
700- If ENTRY-TYPE is 'package' or 'output':
701 'id', 'name', 'regexp', 'all-available', 'newest-available',
702 'installed', 'obsolete', 'generation'.
703
704- If ENTRY-TYPE is 'generation':
189cea27 705 'id', 'last', 'all', 'time'.
81b339fe
AK
706
707PARAMS is a list of parameters for receiving. If it is an empty list,
708get information with all available parameters, which are:
709
710- If ENTRY-TYPE is 'package':
711 'id', 'name', 'version', 'outputs', 'license', 'synopsis',
712 'description', 'home-url', 'inputs', 'native-inputs',
713 'propagated-inputs', 'location', 'installed'.
714
715- If ENTRY-TYPE is 'output':
716 'id', 'package-id', 'name', 'version', 'output', 'license',
717 'synopsis', 'description', 'home-url', 'inputs', 'native-inputs',
718 'propagated-inputs', 'location', 'installed', 'path', 'dependencies'.
719
720- If ENTRY-TYPE is 'generation':
721 'id', 'number', 'prev-number', 'path', 'time'.
722
723Returning value is a list of alists. Each alist consists of
724parameter/value pairs."
725 (case entry-type
726 ((package output)
727 (package/output-sexps profile params entry-type
728 search-type search-vals))
729 ((generation)
730 (generation-sexps profile params
731 search-type search-vals))
732 (else (entry-type-error entry-type))))
457f60fa
AK
733
734\f
81b339fe 735;;; Package actions.
457f60fa
AK
736
737(define* (package->manifest-entry* package #:optional output)
738 (and package
739 (begin
740 (check-package-freshness package)
741 (package->manifest-entry package output))))
742
743(define* (make-install-manifest-entries id #:optional output)
744 (package->manifest-entry* (package-by-id id) output))
745
746(define* (make-upgrade-manifest-entries id #:optional output)
747 (package->manifest-entry* (newest-package-by-id id) output))
748
749(define* (make-manifest-pattern id #:optional output)
750 "Make manifest pattern from a package ID and OUTPUT."
751 (let-values (((name version)
752 (id->name+version id)))
753 (and name version
754 (manifest-pattern
755 (name name)
756 (version version)
757 (output output)))))
758
759(define (convert-action-pattern pattern proc)
760 "Convert action PATTERN into a list of objects returned by PROC.
761PROC is called: (PROC ID) or (PROC ID OUTPUT)."
762 (match pattern
763 ((id . outputs)
764 (if (null? outputs)
765 (let ((obj (proc id)))
766 (if obj (list obj) '()))
767 (filter-map (cut proc id <>)
768 outputs)))
769 (_ '())))
770
771(define (convert-action-patterns patterns proc)
772 (append-map (cut convert-action-pattern <> proc)
773 patterns))
774
775(define* (process-package-actions
776 profile #:key (install '()) (upgrade '()) (remove '())
777 (use-substitutes? #t) dry-run?)
778 "Perform package actions.
779
780INSTALL, UPGRADE, REMOVE are lists of 'package action patterns'.
781Each pattern should have the following form:
782
783 (ID . OUTPUTS)
784
785ID is an object address or a full-name of a package.
786OUTPUTS is a list of package outputs (may be an empty list)."
787 (format #t "The process begins ...~%")
788 (let* ((install (append
789 (convert-action-patterns
790 install make-install-manifest-entries)
791 (convert-action-patterns
792 upgrade make-upgrade-manifest-entries)))
793 (remove (convert-action-patterns remove make-manifest-pattern))
794 (transaction (manifest-transaction (install install)
795 (remove remove)))
796 (manifest (profile-manifest profile))
797 (new-manifest (manifest-perform-transaction
798 manifest transaction)))
799 (unless (and (null? install) (null? remove))
1f06b288
AK
800 (with-store store
801 (let* ((derivation (run-with-store store
802 (profile-derivation new-manifest)))
803 (derivations (list derivation))
804 (new-profile (derivation->output-path derivation)))
805 (set-build-options store
806 #:use-substitutes? use-substitutes?)
5d7a8584 807 (show-manifest-transaction store manifest transaction
1f06b288
AK
808 #:dry-run? dry-run?)
809 (show-what-to-build store derivations
810 #:use-substitutes? use-substitutes?
811 #:dry-run? dry-run?)
812 (unless dry-run?
813 (let ((name (generation-file-name
814 profile
815 (+ 1 (generation-number profile)))))
816 (and (build-derivations store derivations)
817 (let* ((entries (manifest-entries new-manifest))
818 (count (length entries)))
819 (switch-symlinks name new-profile)
820 (switch-symlinks profile name)
821 (format #t (N_ "~a package in profile~%"
822 "~a packages in profile~%"
823 count)
824 count))))))))))
cb6a5c71
AK
825
826(define (delete-generations* profile generations)
827 "Delete GENERATIONS from PROFILE.
828GENERATIONS is a list of generation numbers."
829 (with-store store
830 (delete-generations store profile generations)))