1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2014, 2015, 2016 Alex Kost <alezost@gmail.com>
4 ;;; This file is part of GNU Guix.
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.
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.
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/>.
21 ;; Information about packages and generations is passed to the elisp
22 ;; side in the form of alists of parameters (such as ‘name’ or
23 ;; ‘version’) and their values.
25 ;; ‘entries’ procedure is the “entry point” for the elisp side to get
26 ;; information about packages and generations.
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.
55 (guix scripts package)
59 (define-syntax-rule (first-or-false lst)
60 (and (not (null? lst))
63 (define (list-maybe obj)
64 (if (list? obj) obj (list obj)))
66 (define (output+error thunk)
67 "Call THUNK and return 2 values: output and error output as strings."
68 (let ((output-port (open-output-string))
69 (error-port (open-output-string)))
70 (with-output-to-port output-port
71 (lambda () (with-error-to-port error-port thunk)))
72 (let ((strings (list (get-output-string output-port)
73 (get-output-string error-port))))
74 (close-output-port output-port)
75 (close-output-port error-port)
76 (apply values strings))))
78 (define (full-name->name+version spec)
79 "Given package specification SPEC with or without output,
80 return two values: name and version. For example, for SPEC
81 \"foo@0.9.1b:lib\", return \"foo\" and \"0.9.1b\"."
82 (let-values (((name version output)
83 (package-specification->name+version+output spec)))
84 (values name version)))
86 (define (name+version->full-name name version)
87 (string-append name "@" version))
89 (define* (make-package-specification name #:optional version output)
90 (let ((full-name (if version
91 (name+version->full-name name version)
94 (string-append full-name ":" output)
97 (define (manifest-entry->name+version+output entry)
99 (manifest-entry-name entry)
100 (manifest-entry-version entry)
101 (manifest-entry-output entry)))
103 (define (manifest-entry->package-specification entry)
105 (lambda () (manifest-entry->name+version+output entry))
106 make-package-specification))
108 (define (manifest-entries->package-specifications entries)
109 (map manifest-entry->package-specification entries))
111 (define (profile-package-specifications profile)
112 "Return a list of package specifications for PROFILE."
113 (let ((manifest (profile-manifest profile)))
114 (manifest-entries->package-specifications
115 (manifest-entries manifest))))
117 (define (profile->specifications+paths profile)
118 "Return a list of package specifications and paths for PROFILE.
119 Each element of the list is a list of the package specification and its path."
120 (let ((manifest (profile-manifest profile)))
122 (list (manifest-entry->package-specification entry)
123 (manifest-entry-item entry)))
124 (manifest-entries manifest))))
126 (define (profile-difference profile1 profile2)
127 "Return a list of package specifications for outputs installed in PROFILE1
128 and not installed in PROFILE2."
129 (let ((specs1 (profile-package-specifications profile1))
130 (specs2 (profile-package-specifications profile2)))
131 (lset-difference string=? specs1 specs2)))
133 (define (manifest-entries->hash-table entries)
134 "Return a hash table of name keys and lists of matching manifest ENTRIES."
135 (let ((table (make-hash-table (length entries))))
136 (for-each (lambda (entry)
137 (let* ((key (manifest-entry-name entry))
138 (ref (hash-ref table key)))
140 (if ref (cons entry ref) (list entry)))))
144 (define (manifest=? m1 m2)
148 (define manifest->hash-table
149 (let ((current-manifest #f)
152 "Return a hash table of name keys and matching MANIFEST entries."
153 (unless (manifest=? manifest current-manifest)
154 (set! current-manifest manifest)
155 (set! current-table (manifest-entries->hash-table
156 (manifest-entries manifest))))
159 (define* (manifest-entries-by-name manifest name #:optional version output)
160 "Return a list of MANIFEST entries matching NAME, VERSION and OUTPUT."
161 (let ((entries (or (hash-ref (manifest->hash-table manifest) name)
163 (if (or version output)
164 (filter (lambda (entry)
165 (and (or (not version)
166 (equal? version (manifest-entry-version entry)))
168 (equal? output (manifest-entry-output entry)))))
172 (define (manifest-entry-by-output entries output)
173 "Return a manifest entry from ENTRIES matching OUTPUT."
174 (find (lambda (entry)
175 (string= output (manifest-entry-output entry)))
178 (define (fold-manifest-by-name manifest proc init)
179 "Fold over MANIFEST entries.
180 Call (PROC NAME VERSION ENTRIES RESULT), using INIT as the initial value
181 of RESULT. ENTRIES is a list of manifest entries with NAME/VERSION."
182 (hash-fold (lambda (name entries res)
183 (proc name (manifest-entry-version (car entries))
186 (manifest->hash-table manifest)))
188 (define* (object-transformer param-alist #:optional (params '()))
189 "Return procedure transforming objects into alist of parameter/value pairs.
191 PARAM-ALIST is alist of available parameters (symbols) and procedures
192 returning values of these parameters. Each procedure is applied to
195 PARAMS is list of parameters from PARAM-ALIST that should be returned by
196 a resulting procedure. If PARAMS is not specified or is an empty list,
197 use all available parameters.
201 (let* ((alist `((plus1 . ,1+) (minus1 . ,1-) (mul2 . ,(cut * 2 <>))))
202 (number->alist (object-transformer alist '(plus1 mul2))))
205 ((plus1 . 9) (mul2 . 16))
207 (let* ((use-all-params (null? params))
208 (alist (filter-map (match-lambda
210 (and (or use-all-params
218 (cons param (apply proc objects))))
221 (define %manifest-entry-param-alist
222 `((output . ,manifest-entry-output)
223 (path . ,manifest-entry-item)
224 (dependencies . ,manifest-entry-dependencies)))
226 (define manifest-entry->sexp
227 (object-transformer %manifest-entry-param-alist))
229 (define (manifest-entries->sexps entries)
230 (map manifest-entry->sexp entries))
232 (define (package-inputs-names inputs)
233 "Return a list of full names of the packages from package INPUTS."
234 (filter-map (match-lambda
235 ((_ (? package? package))
236 (make-package-specification (package-name package)
237 (package-version package)))
238 ((_ (? package? package) output)
239 (make-package-specification (package-name package)
240 (package-version package)
245 (define (package-license-names package)
246 "Return a list of license names of the PACKAGE."
247 (filter-map (lambda (license)
248 (and (license? license)
249 (license-name license)))
250 (list-maybe (package-license package))))
252 (define (package-source-names package)
253 "Return a list of source names (URLs) of the PACKAGE."
254 (let ((source (package-source package)))
255 (and (origin? source)
256 (filter-map (lambda (uri)
259 ((git-reference? uri)
260 (git-reference-url uri))
261 (else "Unknown source type")))
262 (list-maybe (origin-uri source))))))
264 (define (package-unique? package)
265 "Return #t if PACKAGE is a single package with such name/version."
266 (match (packages-by-name (package-name package)
267 (package-version package))
271 (define %package-param-alist
272 `((id . ,object-address)
273 (package-id . ,object-address)
274 (name . ,package-name)
275 (version . ,package-version)
276 (license . ,package-license-names)
277 (source . ,package-source-names)
278 (synopsis . ,package-synopsis)
279 (description . ,package-description-string)
280 (home-url . ,package-home-page)
281 (outputs . ,package-outputs)
282 (systems . ,package-supported-systems)
283 (non-unique . ,(negate package-unique?))
284 (inputs . ,(lambda (pkg)
285 (package-inputs-names
286 (package-inputs pkg))))
287 (native-inputs . ,(lambda (pkg)
288 (package-inputs-names
289 (package-native-inputs pkg))))
290 (propagated-inputs . ,(lambda (pkg)
291 (package-inputs-names
292 (package-propagated-inputs pkg))))
293 (location . ,(lambda (pkg)
294 (location->string (package-location pkg))))))
296 (define (package-param package param)
297 "Return a value of a PACKAGE PARAM."
298 (and=> (assq-ref %package-param-alist param)
302 ;;; Finding packages.
304 (define-values (package-by-address
306 (let ((table (delay (fold-packages
307 (lambda (package table)
308 (vhash-consq (object-address package)
313 "Return package by its object ADDRESS."
314 (match (vhash-assq address (force table))
315 ((_ . package) package)
318 "Register PACKAGE by its 'object-address', so that later
319 'package-by-address' can be used to access it."
320 (let ((table* (force table)))
322 (delay (vhash-consq (object-address package)
323 package table*))))))))
325 (define packages-by-name+version
326 (let ((table (delay (fold-packages
327 (lambda (package table)
328 (let ((file (location-file
329 (package-location package))))
330 (vhash-cons (cons (package-name package)
331 (package-version package))
334 (lambda (name version)
335 "Return packages matching NAME and VERSION."
336 (vhash-fold* cons '() (cons name version) (force table)))))
338 (define (packages-by-full-name full-name)
340 (lambda () (full-name->name+version full-name))
341 packages-by-name+version))
343 (define (packages-by-id id)
345 (let ((pkg (package-by-address id)))
346 (if pkg (list pkg) '()))
347 (packages-by-full-name id)))
349 (define (id->name+version id)
351 (and=> (package-by-address id)
353 (values (package-name pkg)
354 (package-version pkg))))
355 (full-name->name+version id)))
357 (define (package-by-id id)
358 (first-or-false (packages-by-id id)))
360 (define (newest-package-by-id id)
361 (and=> (id->name+version id)
363 (first-or-false (find-best-packages-by-name name #f)))))
365 (define (matching-packages predicate)
366 (fold-packages (lambda (pkg res)
372 (define (filter-packages-by-output packages output)
373 (filter (lambda (package)
374 (member output (package-outputs package)))
377 (define* (packages-by-name name #:optional version output)
378 "Return a list of packages matching NAME, VERSION and OUTPUT."
379 (let ((packages (if version
380 (packages-by-name+version name version)
382 (lambda (pkg) (string=? name (package-name pkg)))))))
384 (filter-packages-by-output packages output)
387 (define (manifest-entry->packages entry)
389 (lambda () (manifest-entry->name+version+output entry))
392 (define (packages-by-regexp regexp match-params)
393 "Return a list of packages matching REGEXP string.
394 MATCH-PARAMS is a list of parameters that REGEXP can match."
395 (define (package-match? package regexp)
397 (let ((val (package-param package param)))
398 (and (string? val) (regexp-exec regexp val))))
401 (let ((re (make-regexp regexp regexp/icase)))
402 (matching-packages (cut package-match? <> re))))
404 (define (packages-by-license license)
405 "Return a list of packages with LICENSE."
408 (memq license (list-maybe (package-license package))))))
410 (define (all-available-packages)
411 "Return a list of all available packages."
412 (matching-packages (const #t)))
414 (define (newest-available-packages)
415 "Return a list of the newest available packages."
416 (vhash-fold (lambda (name elem res)
421 (find-newest-available-packages)))
423 (define (packages-from-file file)
424 "Return a list of packages from FILE."
425 (let ((package (load (canonicalize-path file))))
426 (if (package? package)
428 (register-package package)
433 ;;; Making package/output patterns.
435 (define (specification->package-pattern specification)
438 (full-name->name+version specification))
441 (define (specification->output-pattern specification)
444 (package-specification->name+version+output specification #f))
447 (define (id->package-pattern id)
449 (package-by-address id)
450 (specification->package-pattern id)))
452 (define (id->output-pattern id)
453 "Return an output pattern by output ID.
454 ID should be '<package-address>:<output>' or '<name>-<version>:<output>'."
455 (let-values (((name version output)
456 (package-specification->name+version+output id)))
458 (list name version output)
459 (list (package-by-address (string->number name))
462 (define (specifications->package-patterns . specifications)
463 (map specification->package-pattern specifications))
465 (define (specifications->output-patterns . specifications)
466 (map specification->output-pattern specifications))
468 (define (ids->package-patterns . ids)
469 (map id->package-pattern ids))
471 (define (ids->output-patterns . ids)
472 (map id->output-pattern ids))
474 (define* (manifest-patterns-result packages res obsolete-pattern
475 #:optional installed-pattern)
476 "Auxiliary procedure for 'manifest-package-patterns' and
477 'manifest-output-patterns'."
479 (cons (obsolete-pattern) res)
480 (if installed-pattern
481 ;; We don't need duplicates for a list of installed packages,
482 ;; so just take any (car) package.
483 (cons (installed-pattern (car packages)) res)
486 (define* (manifest-package-patterns manifest #:optional obsolete-only?)
487 "Return a list of package patterns for MANIFEST entries.
488 If OBSOLETE-ONLY? is #f, use all entries, otherwise make patterns only
489 for obsolete packages."
490 (fold-manifest-by-name
492 (lambda (name version entries res)
493 (manifest-patterns-result (packages-by-name name version)
495 (lambda () (list name version entries))
496 (and (not obsolete-only?)
497 (cut list <> entries))))
500 (define* (manifest-output-patterns manifest #:optional obsolete-only?)
501 "Return a list of output patterns for MANIFEST entries.
502 If OBSOLETE-ONLY? is #f, use all entries, otherwise make patterns only
503 for obsolete packages."
504 (fold (lambda (entry res)
505 (manifest-patterns-result (manifest-entry->packages entry)
508 (and (not obsolete-only?)
509 (cut list <> entry))))
511 (manifest-entries manifest)))
513 (define (obsolete-package-patterns manifest)
514 (manifest-package-patterns manifest #t))
516 (define (obsolete-output-patterns manifest)
517 (manifest-output-patterns manifest #t))
520 ;;; Transforming package/output patterns into alists.
522 (define (obsolete-package-sexp name version entries)
523 "Return an alist with information about obsolete package.
524 ENTRIES is a list of installed manifest entries."
525 `((id . ,(name+version->full-name name version))
528 (outputs . ,(map manifest-entry-output entries))
530 (installed . ,(manifest-entries->sexps entries))))
532 (define (package-pattern-transformer manifest params)
533 "Return 'package-pattern->package-sexps' procedure."
534 (define package->sexp
535 (object-transformer %package-param-alist params))
537 (define* (sexp-by-package package #:optional
538 (entries (manifest-entries-by-name
540 (package-name package)
541 (package-version package))))
542 (cons (cons 'installed (manifest-entries->sexps entries))
543 (package->sexp package)))
545 (define (->sexps pattern)
547 ((? package? package)
548 (list (sexp-by-package package)))
549 (((? package? package) entries)
550 (list (sexp-by-package package entries)))
551 ((name version entries)
552 (list (obsolete-package-sexp
553 name version entries)))
555 (let ((packages (packages-by-name name version)))
557 (let ((entries (manifest-entries-by-name
558 manifest name version)))
561 (list (obsolete-package-sexp
562 name version entries))))
563 (map sexp-by-package packages))))
568 (define (output-pattern-transformer manifest params)
569 "Return 'output-pattern->output-sexps' procedure."
570 (define package->sexp
571 (object-transformer (alist-delete 'id %package-param-alist)
574 (define manifest-entry->sexp
575 (object-transformer (alist-delete 'output %manifest-entry-param-alist)
578 (define* (output-sexp pkg-alist pkg-address output
580 (let ((entry-alist (if entry
581 (manifest-entry->sexp entry)
583 (base `((id . ,(string-append
584 (number->string pkg-address)
587 (installed . ,(->bool entry)))))
588 (append entry-alist base pkg-alist)))
590 (define (obsolete-output-sexp entry)
591 (let-values (((name version output)
592 (manifest-entry->name+version+output entry)))
593 (let ((base `((id . ,(make-package-specification
594 name version output))
595 (package-id . ,(name+version->full-name name version))
601 (append (manifest-entry->sexp entry) base))))
603 (define* (sexps-by-package package #:optional output
604 (entries (manifest-entries-by-name
606 (package-name package)
607 (package-version package))))
608 ;; Assuming that PACKAGE has this OUTPUT.
609 (let ((pkg-alist (package->sexp package))
610 (address (object-address package))
613 (package-outputs package))))
614 (map (lambda (output)
615 (output-sexp pkg-alist address output
616 (manifest-entry-by-output entries output)))
619 (define* (sexps-by-manifest-entry entry #:optional
620 (packages (manifest-entry->packages
623 (list (obsolete-output-sexp entry))
624 (map (lambda (package)
625 (output-sexp (package->sexp package)
626 (object-address package)
627 (manifest-entry-output entry)
631 (define (->sexps pattern)
633 ((? package? package)
634 (sexps-by-package package))
635 ((package (? string? output))
636 (sexps-by-package package output))
637 ((? manifest-entry? entry)
638 (list (obsolete-output-sexp entry)))
640 (sexps-by-manifest-entry entry (list package)))
641 ((name version output)
642 (let ((packages (packages-by-name name version output)))
644 (let ((entries (manifest-entries-by-name
645 manifest name version output)))
646 (append-map (cut sexps-by-manifest-entry <>)
648 (append-map (cut sexps-by-package <> output)
654 (define (entry-type-error entry-type)
655 (error (format #f "Wrong entry-type '~a'" entry-type)))
657 (define (search-type-error entry-type search-type)
658 (error (format #f "Wrong search type '~a' for entry-type '~a'"
659 search-type entry-type)))
661 (define %pattern-transformers
662 `((package . ,package-pattern-transformer)
663 (output . ,output-pattern-transformer)))
665 (define (pattern-transformer entry-type)
666 (assq-ref %pattern-transformers entry-type))
668 ;; All procedures from inner alists are called with (MANIFEST . SEARCH-VALS)
669 ;; as arguments; see `package/output-sexps'.
670 (define %patterns-makers
671 (let* ((apply-to-rest (lambda (proc)
672 (lambda (_ . rest) (apply proc rest))))
673 (apply-to-first (lambda (proc)
674 (lambda (first . _) (proc first))))
675 (manifest-package-proc (apply-to-first manifest-package-patterns))
676 (manifest-output-proc (apply-to-first manifest-output-patterns))
677 (regexp-proc (lambda (_ regexp params . __)
678 (packages-by-regexp regexp params)))
679 (license-proc (lambda (_ license-name)
681 (lookup-license license-name))))
682 (location-proc (lambda (_ location)
683 (packages-by-location-file location)))
684 (file-proc (lambda (_ file)
685 (packages-from-file file)))
686 (all-proc (lambda _ (all-available-packages)))
687 (newest-proc (lambda _ (newest-available-packages))))
689 (id . ,(apply-to-rest ids->package-patterns))
690 (name . ,(apply-to-rest specifications->package-patterns))
691 (installed . ,manifest-package-proc)
692 (obsolete . ,(apply-to-first obsolete-package-patterns))
693 (regexp . ,regexp-proc)
694 (license . ,license-proc)
695 (location . ,location-proc)
696 (from-file . ,file-proc)
697 (all-available . ,all-proc)
698 (newest-available . ,newest-proc))
700 (id . ,(apply-to-rest ids->output-patterns))
701 (name . ,(apply-to-rest specifications->output-patterns))
702 (installed . ,manifest-output-proc)
703 (obsolete . ,(apply-to-first obsolete-output-patterns))
704 (regexp . ,regexp-proc)
705 (license . ,license-proc)
706 (location . ,location-proc)
707 (from-file . ,file-proc)
708 (all-available . ,all-proc)
709 (newest-available . ,newest-proc)))))
711 (define (patterns-maker entry-type search-type)
712 (or (and=> (assq-ref %patterns-makers entry-type)
713 (cut assq-ref <> search-type))
714 (search-type-error entry-type search-type)))
716 (define (package/output-sexps profile params entry-type
717 search-type search-vals)
718 "Return information about packages or package outputs.
719 See 'entry-sexps' for details."
720 (let* ((manifest (profile-manifest profile))
721 (patterns (if (and (eq? entry-type 'output)
722 (eq? search-type 'profile-diff))
725 (map specification->output-pattern
726 (profile-difference p1 p2)))
728 (apply (patterns-maker entry-type search-type)
729 manifest search-vals)))
730 (->sexps ((pattern-transformer entry-type) manifest params)))
731 (append-map ->sexps patterns)))
734 ;;; Getting information about generations.
736 (define (generation-param-alist profile)
737 "Return an alist of generation parameters and procedures for PROFILE."
738 (let ((current (generation-number profile)))
741 (prev-number . ,(cut previous-generation-number profile <>))
742 (current . ,(cut = current <>))
743 (path . ,(cut generation-file-name profile <>))
744 (time . ,(lambda (gen)
745 (time-second (generation-time profile gen)))))))
747 (define (matching-generations profile predicate)
748 "Return a list of PROFILE generations matching PREDICATE."
749 (filter predicate (profile-generations profile)))
751 (define (last-generations profile number)
752 "Return a list of last NUMBER generations.
753 If NUMBER is 0 or less, return all generations."
754 (let ((generations (profile-generations profile))
755 (number (if (<= number 0) +inf.0 number)))
756 (if (> (length generations) number)
757 (list-head (reverse generations) number)
760 (define (find-generations profile search-type search-vals)
761 "Find PROFILE's generations matching SEARCH-TYPE and SEARCH-VALS."
764 (matching-generations profile (cut memq <> search-vals)))
766 (last-generations profile (car search-vals)))
768 (last-generations profile +inf.0))
772 (matching-generations
775 (let ((time (time-second (generation-time profile gen))))
778 (else (search-type-error "generation" search-type))))
780 (define (generation-sexps profile params search-type search-vals)
781 "Return information about generations.
782 See 'entry-sexps' for details."
783 (let ((generations (find-generations profile search-type search-vals))
784 (->sexp (object-transformer (generation-param-alist profile)
786 (map ->sexp generations)))
788 (define system-generation-boot-parameters
790 (lambda (profile generation)
791 "Return boot parameters for PROFILE's system GENERATION."
792 (let* ((gen-file (generation-file-name profile generation))
793 (param-file (string-append gen-file "/parameters")))
794 (call-with-input-file param-file read-boot-parameters)))))
796 (define (system-generation-param-alist profile)
797 "Return an alist of system generation parameters and procedures for
799 (append (generation-param-alist profile)
800 `((label . ,(lambda (gen)
801 (boot-parameters-label
802 (system-generation-boot-parameters
804 (root-device . ,(lambda (gen)
805 (boot-parameters-root-device
806 (system-generation-boot-parameters
808 (kernel . ,(lambda (gen)
809 (boot-parameters-kernel
810 (system-generation-boot-parameters
813 (define (system-generation-sexps profile params search-type search-vals)
814 "Return an alist with information about system generations."
815 (let ((generations (find-generations profile search-type search-vals))
816 (->sexp (object-transformer (system-generation-param-alist profile)
818 (map ->sexp generations)))
821 ;;; Getting package/output/generation entries (alists).
823 (define (entries profile params entry-type search-type search-vals)
824 "Return information about entries.
826 ENTRY-TYPE is a symbol defining a type of returning information. Should
827 be: 'package', 'output' or 'generation'.
829 SEARCH-TYPE and SEARCH-VALS define how to get the information.
830 SEARCH-TYPE should be one of the following symbols:
832 - If ENTRY-TYPE is 'package' or 'output':
833 'id', 'name', 'regexp', 'all-available', 'newest-available',
834 'installed', 'obsolete', 'generation'.
836 - If ENTRY-TYPE is 'generation':
837 'id', 'last', 'all', 'time'.
839 PARAMS is a list of parameters for receiving. If it is an empty list,
840 get information with all available parameters, which are:
842 - If ENTRY-TYPE is 'package':
843 'id', 'name', 'version', 'outputs', 'license', 'synopsis',
844 'description', 'home-url', 'inputs', 'native-inputs',
845 'propagated-inputs', 'location', 'installed'.
847 - If ENTRY-TYPE is 'output':
848 'id', 'package-id', 'name', 'version', 'output', 'license',
849 'synopsis', 'description', 'home-url', 'inputs', 'native-inputs',
850 'propagated-inputs', 'location', 'installed', 'path', 'dependencies'.
852 - If ENTRY-TYPE is 'generation':
853 'id', 'number', 'prev-number', 'path', 'time'.
855 Returning value is a list of alists. Each alist consists of
856 parameter/value pairs."
859 (package/output-sexps profile params entry-type
860 search-type search-vals))
862 (generation-sexps profile params
863 search-type search-vals))
865 (system-generation-sexps profile params
866 search-type search-vals))
867 (else (entry-type-error entry-type))))
872 (define* (package->manifest-entry* package #:optional output)
874 (package->manifest-entry package output)))
876 (define* (make-install-manifest-entries id #:optional output)
877 (package->manifest-entry* (package-by-id id) output))
879 (define* (make-upgrade-manifest-entries id #:optional output)
880 (package->manifest-entry* (newest-package-by-id id) output))
882 (define* (make-manifest-pattern id #:optional output)
883 "Make manifest pattern from a package ID and OUTPUT."
884 (let-values (((name version)
885 (id->name+version id)))
892 (define (convert-action-pattern pattern proc)
893 "Convert action PATTERN into a list of objects returned by PROC.
894 PROC is called: (PROC ID) or (PROC ID OUTPUT)."
898 (let ((obj (proc id)))
899 (if obj (list obj) '()))
900 (filter-map (cut proc id <>)
904 (define (convert-action-patterns patterns proc)
905 (append-map (cut convert-action-pattern <> proc)
908 (define* (process-package-actions
909 profile #:key (install '()) (upgrade '()) (remove '())
910 (use-substitutes? #t) dry-run?)
911 "Perform package actions.
913 INSTALL, UPGRADE, REMOVE are lists of 'package action patterns'.
914 Each pattern should have the following form:
918 ID is an object address or a full-name of a package.
919 OUTPUTS is a list of package outputs (may be an empty list)."
920 (format #t "The process begins ...~%")
921 (let* ((install (append
922 (convert-action-patterns
923 install make-install-manifest-entries)
924 (convert-action-patterns
925 upgrade make-upgrade-manifest-entries)))
926 (remove (convert-action-patterns remove make-manifest-pattern))
927 (transaction (manifest-transaction (install install)
929 (manifest (profile-manifest profile))
930 (new-manifest (manifest-perform-transaction
931 manifest transaction)))
932 (unless (and (null? install) (null? remove))
934 (set-build-options store
935 #:print-build-trace #f
936 #:use-substitutes? use-substitutes?)
937 (show-manifest-transaction store manifest transaction
939 (build-and-use-profile store profile new-manifest
940 #:use-substitutes? use-substitutes?
941 #:dry-run? dry-run?)))))
943 (define (delete-generations* profile generations)
944 "Delete GENERATIONS from PROFILE.
945 GENERATIONS is a list of generation numbers."
947 (delete-generations store profile generations)))
949 (define (package-location-string id-or-name)
950 "Return a location string of a package with ID-OR-NAME."
951 (and=> (or (package-by-id id-or-name)
952 (match (packages-by-name id-or-name)
954 ((package _ ...) package)))
955 (compose location->string package-location)))
957 (define (package-store-path package-id)
958 "Return a list of store directories of outputs of package PACKAGE-ID."
959 (match (package-by-id package-id)
965 (derivation-output-path drv)))
966 (derivation-outputs (package-derivation store package)))))))
968 (define (package-source-derivation->store-path derivation)
969 "Return a store path of the package source DERIVATION."
970 (match (derivation-outputs derivation)
971 ;; Source derivation is always (("out" . derivation)).
973 (derivation-output-path output-drv))
976 (define (package-source-path package-id)
977 "Return a store file path to a source of a package PACKAGE-ID."
978 (and-let* ((package (package-by-id package-id))
979 (source (package-source package)))
981 (package-source-derivation->store-path
982 (package-source-derivation store source)))))
984 (define* (package-source-build-derivation package-id #:key dry-run?
985 (use-substitutes? #t))
986 "Build source derivation of a package PACKAGE-ID."
987 (and-let* ((package (package-by-id package-id))
988 (source (package-source package)))
990 (let* ((derivation (package-source-derivation store source))
991 (derivations (list derivation)))
992 (set-build-options store
993 #:print-build-trace #f
994 #:use-substitutes? use-substitutes?)
995 (show-what-to-build store derivations
996 #:use-substitutes? use-substitutes?
999 (build-derivations store derivations))
1000 (format #t "The source store path: ~a~%"
1001 (package-source-derivation->store-path derivation))))))
1003 (define (package-build-log-file package-id)
1004 "Return the build log file of a package PACKAGE-ID.
1005 Return #f if the build log is not found."
1006 (and-let* ((package (package-by-id package-id)))
1008 (let* ((derivation (package-derivation store package))
1009 (file (derivation-file-name derivation)))
1010 (or (log-file store file)
1011 ((@@ (guix scripts build) log-url) store file))))))
1014 ;;; Executing guix commands
1016 (define (guix-command . args)
1017 "Run 'guix ARGS ...' command."
1019 (lambda () (apply run-guix args))
1022 (define (guix-command-output . args)
1023 "Return 2 strings with 'guix ARGS ...' output and error output."
1026 (parameterize ((guix-warning-port (current-error-port)))
1027 (apply guix-command args)))))
1029 (define (help-string . commands)
1030 "Return string with 'guix COMMANDS ... --help' output."
1031 (apply guix-command-output `(,@commands "--help")))
1033 (define (pipe-guix-output guix-args command-args)
1034 "Run 'guix GUIX-ARGS ...' command and pipe its output to a shell command
1035 defined by COMMAND-ARGS.
1036 Return #t if the shell command was executed successfully."
1037 (let ((pipe (apply open-pipe* OPEN_WRITE command-args)))
1038 (with-output-to-port pipe
1039 (lambda () (apply guix-command guix-args)))
1040 (zero? (status:exit-val (close-pipe pipe)))))
1043 ;;; Lists of packages, lint checkers, etc.
1045 (define (graph-type-names)
1046 "Return a list of names of available graph node types."
1047 (map (@ (guix graph) node-type-name)
1048 (@ (guix scripts graph) %node-types)))
1050 (define (refresh-updater-names)
1051 "Return a list of names of available refresh updater types."
1052 (map (@ (guix upstream) upstream-updater-name)
1053 (@ (guix scripts refresh) %updaters)))
1055 (define (lint-checker-names)
1056 "Return a list of names of available lint checkers."
1057 (map (lambda (checker)
1058 (symbol->string ((@ (guix scripts lint) lint-checker-name)
1060 (@ (guix scripts lint) %checkers)))
1062 (define (package-names)
1063 "Return a list of names of available packages."
1065 (fold-packages (lambda (pkg res)
1066 (cons (package-name pkg) res))
1069 ;; See the comment to 'guix-package-names' function in "guix-popup.el".
1070 (define (package-names-lists)
1071 (map list (package-names)))
1079 (module-map (lambda (_ var)
1081 (resolve-interface '(guix licenses))))))
1086 (define (license-names)
1087 "Return a list of names of available licenses."
1088 (map license-name (licenses)))
1090 (define lookup-license
1093 "Return a license by its name."
1095 (string=? name (license-name l)))
1098 (define (lookup-license-uri name)
1099 "Return a license URI by its name."
1100 (and=> (lookup-license name)
1103 (define %license-param-alist
1104 `((id . ,license-name)
1105 (name . ,license-name)
1106 (url . ,license-uri)
1107 (comment . ,license-comment)))
1109 (define license->sexp
1110 (object-transformer %license-param-alist))
1112 (define (find-licenses search-type . search-values)
1113 "Return a list of licenses depending on SEARCH-TYPE and SEARCH-VALUES."
1116 (let ((names search-values))
1117 (filter-map lookup-license names)))
1121 (define (license-entries search-type . search-values)
1123 (apply find-licenses search-type search-values)))
1126 ;;; Package locations
1128 (define-values (packages-by-location-file
1129 package-location-files)
1130 (let* ((table (delay (fold-packages
1131 (lambda (package table)
1132 (let ((file (location-file
1133 (package-location package))))
1134 (vhash-cons file package table)))
1136 (files (delay (vhash-fold
1137 (lambda (file _ result)
1138 (if (member file result)
1140 (cons file result)))
1145 "Return the (possibly empty) list of packages defined in location FILE."
1146 (vhash-fold* cons '() file (force table)))
1148 "Return the list of file names of all package locations."
1151 (define %package-location-param-alist
1153 (location . ,identity)
1154 (number-of-packages . ,(lambda (location)
1155 (length (packages-by-location-file location))))))
1157 (define package-location->sexp
1158 (object-transformer %package-location-param-alist))
1160 (define (package-location-entries)
1161 (map package-location->sexp (package-location-files)))