gnu: linux-libre 4.19: Update to 4.19.196.
[jackhill/guix/guix.git] / guix / ui.scm
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
3 ;;; Copyright © 2013, 2018 Mark H Weaver <mhw@netris.org>
4 ;;; Copyright © 2013 Nikita Karetnikov <nikita@karetnikov.org>
5 ;;; Copyright © 2014 Cyril Roelandt <tipecaml@gmail.com>
6 ;;; Copyright © 2014 Cyrill Schenkel <cyrill.schenkel@gmail.com>
7 ;;; Copyright © 2014, 2015, 2017 Alex Kost <alezost@gmail.com>
8 ;;; Copyright © 2015 David Thompson <davet@gnu.org>
9 ;;; Copyright © 2015, 2016 Mathieu Lirzin <mthl@gnu.org>
10 ;;; Copyright © 2016 Roel Janssen <roel@gnu.org>
11 ;;; Copyright © 2016 Benz Schenk <benz.schenk@uzh.ch>
12 ;;; Copyright © 2018 Kyle Meyer <kyle@kyleam.com>
13 ;;; Copyright © 2018 Ricardo Wurmus <rekado@elephly.net>
14 ;;; Copyright © 2019 Chris Marusich <cmmarusich@gmail.com>
15 ;;; Copyright © 2019, 2020 Tobias Geerinckx-Rice <me@tobias.gr>
16 ;;; Copyright © 2019, 2021 Simon Tournier <zimon.toutoune@gmail.com>
17 ;;; Copyright © 2020 Arun Isaac <arunisaac@systemreboot.net>
18 ;;; Copyright © 2020 Maxim Cournoyer <maxim.cournoyer@gmail.com>
19 ;;;
20 ;;; This file is part of GNU Guix.
21 ;;;
22 ;;; GNU Guix is free software; you can redistribute it and/or modify it
23 ;;; under the terms of the GNU General Public License as published by
24 ;;; the Free Software Foundation; either version 3 of the License, or (at
25 ;;; your option) any later version.
26 ;;;
27 ;;; GNU Guix is distributed in the hope that it will be useful, but
28 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
29 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 ;;; GNU General Public License for more details.
31 ;;;
32 ;;; You should have received a copy of the GNU General Public License
33 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
34
35 (define-module (guix ui)
36 #:use-module (guix i18n)
37 #:use-module (guix colors)
38 #:use-module (guix diagnostics)
39 #:use-module (guix gexp)
40 #:use-module (guix sets)
41 #:use-module (guix utils)
42 #:use-module (guix store)
43 #:use-module (guix config)
44 #:use-module (guix packages)
45 #:use-module (guix profiles)
46 #:use-module (guix derivations)
47 #:use-module (guix build-system)
48 #:use-module (guix serialization)
49 #:use-module ((guix licenses)
50 #:select (license? license-name license-uri))
51 #:use-module ((guix build syscalls)
52 #:select (free-disk-space terminal-columns terminal-rows
53 with-file-lock/no-wait))
54 #:use-module ((guix build utils)
55 ;; XXX: All we need are the bindings related to
56 ;; '&invoke-error'. However, to work around the bug described
57 ;; in 5d669883ecc104403c5d3ba7d172e9c02234577c, #:hide
58 ;; unwanted bindings instead of #:select'ing the needed
59 ;; bindings.
60 #:hide (package-name->name+version
61 ;; Avoid "overrides core binding" warning.
62 delete))
63 #:use-module (srfi srfi-1)
64 #:use-module (srfi srfi-9 gnu)
65 #:use-module (srfi srfi-11)
66 #:use-module (srfi srfi-19)
67 #:use-module (srfi srfi-26)
68 #:use-module (srfi srfi-31)
69 #:use-module (srfi srfi-34)
70 #:use-module (srfi srfi-35)
71 #:autoload (ice-9 ftw) (scandir)
72 #:use-module (ice-9 match)
73 #:use-module (ice-9 format)
74 #:use-module (ice-9 regex)
75 #:autoload (ice-9 popen) (open-pipe* close-pipe)
76 #:autoload (system repl repl) (start-repl)
77 #:autoload (system repl debug) (make-debug stack->vector)
78 #:autoload (web uri) (encode-and-join-uri-path)
79 #:use-module (texinfo)
80 #:use-module (texinfo plain-text)
81 #:use-module (texinfo string-utils)
82
83 ;; Re-exports for backward compatibility.
84 #:re-export (G_ N_ P_ ;now in (guix i18n)
85
86 warning info report-error leave ;now in (guix diagnostics)
87 location->string
88 guix-warning-port program-name)
89 #:export (display-hint
90 make-user-module
91 load*
92 warn-about-load-error
93 show-version-and-exit
94 show-bug-report-information
95 make-regexp*
96 string->number*
97 size->number
98 show-derivation-outputs
99 build-notifier
100 show-what-to-build
101 show-what-to-build*
102 show-manifest-transaction
103 guard*
104 call-with-error-handling
105 with-error-handling
106 with-unbound-variable-handling
107 leave-on-EPIPE
108 read/eval
109 read/eval-package-expression
110 check-available-space
111 indented-string
112 fill-paragraph
113 %text-width
114 texi->plain-text
115 package-description-string
116 package-synopsis-string
117 string->recutils
118 package->recutils
119 package-specification->name+version+output
120
121 supports-hyperlinks?
122 hyperlink
123 file-hyperlink
124 location->hyperlink
125
126 with-paginated-output-port
127 relevance
128 package-relevance
129 display-search-results
130
131 with-profile-lock
132 string->generations
133 string->duration
134 matching-generations
135 display-generation
136 display-profile-content
137 display-profile-content-diff
138 roll-back*
139 switch-to-generation*
140 delete-generation*
141
142 %default-message-language
143 current-message-language
144
145 run-guix-command
146 run-guix
147 guix-main))
148
149 ;;; Commentary:
150 ;;;
151 ;;; User interface facilities for command-line tools.
152 ;;;
153 ;;; Code:
154
155 (define (print-unbound-variable-error port key args default-printer)
156 ;; Print unbound variable errors more nicely, and in the right language.
157 (match args
158 ((proc message (variable) _ ...)
159 ;; We can always omit PROC because when it's useful (i.e., different from
160 ;; "module-lookup"), it gets displayed before.
161 (format port (G_ "error: ~a: unbound variable") variable))
162 (_
163 (default-printer))))
164
165 (set-exception-printer! 'unbound-variable print-unbound-variable-error)
166
167 (define (make-user-module modules)
168 "Return a new user module with the additional MODULES loaded."
169 ;; Module in which the machine description file is loaded.
170 (let ((module (make-fresh-user-module)))
171 (for-each (lambda (iface)
172 (module-use! module (resolve-interface iface)))
173 modules)
174 module))
175
176 (define (last-frame-with-source stack)
177 "Walk stack upwards and return the last frame that has source location
178 information, or #f if it could not be found."
179 (define (frame-with-source frame)
180 ;; Walk from FRAME upwards until source location information is found.
181 (let loop ((frame frame)
182 (previous frame))
183 (if (not frame)
184 previous
185
186 ;; On Guile 3, the latest frame with source may be that of
187 ;; 'raise-exception' in boot-9.scm. Skip it.
188 (if (and (frame-source frame)
189 (not (eq? 'raise-exception (frame-procedure-name frame))))
190 frame
191 (loop (frame-previous frame) frame)))))
192
193 (let* ((depth (stack-length stack))
194 (last (and (> depth 0) (stack-ref stack 0))))
195 (frame-with-source (if (> depth 1)
196 (stack-ref stack 1) ;skip the 'throw' frame
197 last))))
198
199 (define-syntax-rule (without-compiler-optimizations exp)
200 ;; Compile with the baseline compiler (-O1), which is much less expensive
201 ;; than -O2.
202 (parameterize (((@ (system base compile) default-optimization-level) 1))
203 exp))
204
205 (define* (load* file user-module
206 #:key (on-error 'nothing-special))
207 "Load the user provided Scheme source code FILE."
208 (define (error-string frame args)
209 (call-with-output-string
210 (lambda (port)
211 (apply display-error frame port (cdr args)))))
212
213 (define tag
214 (make-prompt-tag "user-code"))
215
216 (catch #t
217 (lambda ()
218 ;; XXX: Force a recompilation to avoid ABI issues.
219 (set! %fresh-auto-compile #t)
220 (set! %load-should-auto-compile #t)
221
222 (save-module-excursion
223 (lambda ()
224 (set-current-module user-module)
225
226 ;; Hide the "auto-compiling" messages.
227 (parameterize ((current-warning-port (%make-void-port "w")))
228 (call-with-prompt tag
229 (lambda ()
230 ;; Give 'load' an absolute file name so that it doesn't try to
231 ;; search for FILE in %LOAD-PATH. Note: use 'load', not
232 ;; 'primitive-load', so that FILE is compiled, which then allows
233 ;; us to provide better error reporting with source line numbers.
234 (without-compiler-optimizations
235 (load (canonicalize-path file))))
236 (const #f))))))
237 (lambda _
238 ;; XXX: Errors are reported from the pre-unwind handler below, but
239 ;; calling 'exit' from there has no effect, so we call it here.
240 (exit 1))
241 (rec (handle-error . args)
242 ;; Capture the stack up to this procedure call, excluded, and pass
243 ;; the faulty stack frame to 'report-load-error'.
244 (let* ((stack (make-stack #t handle-error tag))
245 (frame (last-frame-with-source stack)))
246
247 (report-load-error file args frame)
248
249 (case on-error
250 ((debug)
251 (newline)
252 (display (G_ "entering debugger; type ',bt' for a backtrace\n"))
253 (start-repl #:debug (make-debug (stack->vector stack) 0
254 (error-string frame args)
255 #f)))
256 ((backtrace)
257 (newline (current-error-port))
258 (display-backtrace stack (current-error-port)))
259 (else
260 #t))))))
261
262 (define (known-variable-definition variable)
263 "Search among the currently loaded modules one that defines a variable named
264 VARIABLE and return it, or #f if none was found."
265 (define (module<? m1 m2)
266 (match (module-name m2)
267 (('gnu _ ...) #t)
268 (('guix _ ...)
269 (match (module-name m1)
270 (('gnu _ ...) #f)
271 (_ #t)))
272 (_ #f)))
273
274 (let loop ((modules (list (resolve-module '() #f #f #:ensure #f)))
275 (suggestions '())
276 (visited (setq)))
277 (match modules
278 (()
279 ;; Pick the "best" suggestion.
280 (match (sort suggestions module<?)
281 (() #f)
282 ((first _ ...) first)))
283 ((head tail ...)
284 (if (set-contains? visited head)
285 (loop tail suggestions visited)
286 (let ((visited (set-insert head visited))
287 (next (append tail
288 (hash-map->list (lambda (name module)
289 module)
290 (module-submodules head)))))
291 (match (and=> (module-public-interface head)
292 (cut module-local-variable <> variable))
293 (#f (loop next suggestions visited))
294 (_
295 (match (module-name head)
296 (('gnu _ ...) head) ;must be that one
297 (_ (loop next (cons head suggestions) visited)))))))))))
298
299 (define %hint-color (color BOLD CYAN))
300
301 (define* (display-hint message #:optional (port (current-error-port)))
302 "Display MESSAGE, a l10n message possibly containing Texinfo markup, to
303 PORT."
304 (define colorize
305 (if (color-output? port)
306 (lambda (str)
307 (colorize-string str %hint-color))
308 identity))
309
310 (display (colorize (G_ "hint: ")) port)
311 (display
312 ;; XXX: We should arrange so that the initial indent is wider.
313 (parameterize ((%text-width (max 15 (- (terminal-columns) 5))))
314 (texi->plain-text message))
315 port))
316
317 (define* (report-unbound-variable-error args #:key frame)
318 "Return the given unbound-variable error, where ARGS is the list of 'throw'
319 arguments."
320 (match args
321 ((key . args)
322 (print-exception (current-error-port) frame key args)))
323 (match args
324 (('unbound-variable proc message (variable) _ ...)
325 (match (known-variable-definition variable)
326 (#f
327 (display-hint (G_ "Did you forget a @code{use-modules} form?")))
328 ((? module? module)
329 (display-hint (format #f (G_ "Did you forget @code{(use-modules ~a)}?")
330 (module-name module))))))))
331
332 (define (check-module-matches-file module file)
333 "Check whether FILE starts with 'define-module MODULE' and print a hint if
334 it doesn't."
335 ;; This is a common mistake when people start writing their own package
336 ;; definitions and try loading them with 'guix build -L …', so help them
337 ;; diagnose the problem.
338 (define (hint)
339 (display-hint (format #f (G_ "File @file{~a} should probably start with:
340
341 @example\n(define-module ~a)\n@end example")
342 file module)))
343
344 (catch 'system-error
345 (lambda ()
346 (let* ((sexp (call-with-input-file file read))
347 (loc (and (pair? sexp)
348 (source-properties->location (source-properties sexp)))))
349 (match sexp
350 (('define-module (names ...) _ ...)
351 (unless (equal? module names)
352 (warning loc
353 (G_ "module name ~a does not match file name '~a'~%")
354 names (module->source-file-name module))
355 (hint)))
356 ((? eof-object?)
357 (warning (G_ "~a: file is empty~%") file))
358 (else
359 (hint)))))
360 (const #f)))
361
362 (define* (report-load-error file args #:optional frame)
363 "Report the failure to load FILE, a user-provided Scheme file.
364 ARGS is the list of arguments received by the 'throw' handler."
365 (match args
366 (('system-error . rest)
367 (let ((err (system-error-errno args)))
368 (report-error (G_ "failed to load '~a': ~a~%") file (strerror err))))
369 (('read-error _ message args ...)
370 ;; Guile's missing-paren messages are obscure so we make them more
371 ;; intelligible here.
372 (if (or (string-suffix? "end of file" message) ;Guile < 3.0.6
373 (and (string-contains message "unexpected end of input")
374 (member '(#\)) args)))
375 (let ((location (string-take message
376 (+ 2 (string-contains message ": ")))))
377 (format (current-error-port) (G_ "~amissing closing parenthesis~%")
378 location))
379 (apply throw args)))
380 (('syntax-error proc message properties form subform . rest)
381 (let ((loc (source-properties->location properties)))
382 (report-error loc (G_ "~s: ~a~%")
383 (or subform form) message)))
384 (('unbound-variable _ ...)
385 (report-unbound-variable-error args #:frame frame))
386 (((or 'srfi-34 '%exception) obj)
387 (cond ((message-condition? obj)
388 (report-error (and (error-location? obj)
389 (error-location obj))
390 (G_ "~a~%")
391 (gettext (condition-message obj) %gettext-domain)))
392 ((formatted-message? obj)
393 (apply report-error
394 (and (error-location? obj) (error-location obj))
395 (gettext (formatted-message-string obj) %gettext-domain)
396 (formatted-message-arguments obj)))
397 (else
398 (report-error (G_ "exception thrown: ~s~%") obj)))
399 (when (fix-hint? obj)
400 (display-hint (condition-fix-hint obj))))
401 ((key args ...)
402 (report-error (G_ "failed to load '~a':~%") file)
403 (match args
404 (((? symbol? proc) (? string? message) (args ...) . rest)
405 (display-error frame (current-error-port) proc message
406 args rest))
407 (_
408 ;; Some exceptions like 'git-error' do not follow Guile's convention
409 ;; above and need to be printed with 'print-exception'.
410 (print-exception (current-error-port) frame key args))))))
411
412 (define (warn-about-load-error file module args) ;FIXME: factorize with ↑
413 "Report the failure to load FILE, a user-provided Scheme file, without
414 exiting. ARGS is the list of arguments received by the 'throw' handler."
415 (match args
416 (('system-error . rest)
417 (let ((err (system-error-errno args)))
418 (warning (G_ "failed to load '~a': ~a~%") module (strerror err))))
419 (('syntax-error proc message properties form . rest)
420 (let ((loc (source-properties->location properties)))
421 (warning loc (G_ "~a~%") message)))
422 (('unbound-variable _ ...)
423 (report-unbound-variable-error args))
424 (((or 'srfi-34 '%exception) obj)
425 (cond ((message-condition? obj)
426 (warning (G_ "failed to load '~a': ~a~%")
427 file
428 (gettext (condition-message obj) %gettext-domain)))
429 ((formatted-message? obj)
430 (warning (G_ "failed to load '~a': ~a~%")
431 file
432 (apply format #f
433 (gettext (formatted-message-string obj)
434 %gettext-domain)
435 (formatted-message-arguments obj))))
436 (else
437 (warning (G_ "failed to load '~a': exception thrown: ~s~%")
438 file obj))))
439 ((error args ...)
440 (warning (G_ "failed to load '~a':~%") module)
441 (apply display-error #f (current-error-port) args)
442 (check-module-matches-file module file))))
443
444 (define (call-with-unbound-variable-handling thunk)
445 (define tag
446 (make-prompt-tag "user-code"))
447
448 (catch 'unbound-variable
449 (lambda ()
450 (call-with-prompt tag
451 thunk
452 (const #f)))
453 (const #t)
454 (rec (handle-error . args)
455 (let* ((stack (make-stack #t handle-error tag))
456 (frame (and stack (last-frame-with-source stack))))
457 (report-unbound-variable-error args #:frame frame)
458 (exit 1)))))
459
460 (define-syntax-rule (with-unbound-variable-handling exp ...)
461 "Capture 'unbound-variable' exceptions in the dynamic extent of EXP... and
462 report them in a user-friendly way."
463 (call-with-unbound-variable-handling (lambda () exp ...)))
464
465 (define %default-message-language
466 ;; Default language to use for messages.
467 (make-parameter "en"))
468
469 (define (current-message-language)
470 "Return the language used for messages according to the current locale.
471 Return %DEFAULT-MESSAGE-LANGUAGE if that information could not be obtained. The
472 result is an ISO-639-2 language code such as \"ar\", without the territory
473 part."
474 (let ((locale (setlocale LC_MESSAGES)))
475 (match (string-index locale #\_)
476 (#f locale)
477 (index (string-take locale index)))))
478
479 (define (install-locale)
480 "Install the current locale settings."
481 (catch 'system-error
482 (lambda _
483 (setlocale LC_ALL ""))
484 (lambda args
485 (display-hint (G_ "Consider installing the @code{glibc-locales} package
486 and defining @code{GUIX_LOCPATH}, along these lines:
487
488 @example
489 guix install glibc-locales
490 export GUIX_LOCPATH=\"$HOME/.guix-profile/lib/locale\"
491 @end example
492
493 See the \"Application Setup\" section in the manual, for more info.\n"))
494 ;; We're now running in the "C" locale. Try to install a UTF-8 locale
495 ;; instead. This one is guaranteed to be available in 'guix' from 'guix
496 ;; pull'.
497 (false-if-exception (setlocale LC_ALL "en_US.utf8")))))
498
499 (define (initialize-guix)
500 "Perform the usual initialization for stand-alone Guix commands."
501 ;; By default don't annoy users with deprecation warnings. In practice,
502 ;; 'define-deprecated' in (ice-9 deprecated) arranges so that those warnings
503 ;; are emitted at expansion-time only, but there are cases where they could
504 ;; slip through, for instance when interpreting code.
505 (unless (getenv "GUILE_WARN_DEPRECATED")
506 (debug-disable 'warn-deprecated))
507
508 (install-locale)
509 (textdomain %gettext-domain)
510
511 ;; Ignore SIGPIPE. If the daemon closes the connection, we prefer to be
512 ;; notified via an EPIPE later.
513 (sigaction SIGPIPE SIG_IGN)
514
515 (setvbuf (current-output-port) 'line)
516 (setvbuf (current-error-port) 'line))
517
518 (define* (show-version-and-exit #:optional (command (car (command-line))))
519 "Display version information for COMMAND and `(exit 0)'."
520 (simple-format #t "~a (~a) ~a~%"
521 command %guix-package-name %guix-version)
522 (format #t "Copyright ~a 2021 ~a"
523 ;; TRANSLATORS: Translate "(C)" to the copyright symbol
524 ;; (C-in-a-circle), if this symbol is available in the user's
525 ;; locale. Otherwise, do not translate "(C)"; leave it as-is. */
526 (G_ "(C)")
527 (G_ "the Guix authors\n"))
528 (display (G_"\
529 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
530 This is free software: you are free to change and redistribute it.
531 There is NO WARRANTY, to the extent permitted by law.
532 "))
533 (exit 0))
534
535 (define (show-bug-report-information)
536 ;; TRANSLATORS: The placeholder indicates the bug-reporting address for this
537 ;; package. Please add another line saying "Report translation bugs to
538 ;; ...\n" with the address for translation bugs (typically your translation
539 ;; team's web or email address).
540 (format #t (G_ "
541 Report bugs to: ~a.") %guix-bug-report-address)
542 (format #t (G_ "
543 ~a home page: <~a>") %guix-package-name %guix-home-page-url)
544 (format #t (G_ "
545 General help using Guix and GNU software: <~a>")
546 ;; TRANSLATORS: Change the "/en" bit of this URL appropriately if
547 ;; the web site is translated in your language.
548 (G_ "https://guix.gnu.org/en/help/"))
549 (newline))
550
551 (define (augmented-system-error-handler file)
552 "Return a 'system-error' handler that mentions FILE in its message."
553 (lambda (key proc fmt args errno)
554 ;; Augment the FMT and ARGS with information about TARGET (this
555 ;; information is missing as of Guile 2.0.11, making the exception
556 ;; uninformative.)
557 (apply throw key proc "~A: ~S"
558 (list (strerror (car errno)) file)
559 (list errno))))
560
561 (define-syntax apply-formals
562 (syntax-rules ()
563 ((_ proc (args ...)) (proc args ...))
564 ((_ proc (arg1 args ... . rest)) (apply proc arg1 args ... rest))))
565
566 (define-syntax-rule (error-reporting-wrapper proc formals file)
567 "Wrap PROC such that its 'system-error' exceptions are augmented to mention
568 FILE."
569 (let ((real-proc (@ (guile) proc)))
570 (lambda formals
571 (catch 'system-error
572 (lambda ()
573 (apply-formals real-proc formals))
574 (augmented-system-error-handler file)))))
575
576 (set! symlink
577 ;; We 'set!' the global binding because (gnu build ...) modules and similar
578 ;; typically don't use (guix ui).
579 (error-reporting-wrapper symlink (source target) target))
580
581 (set! copy-file
582 ;; Note: here we use 'set!', not #:replace, because UIs typically use
583 ;; 'copy-recursively', which doesn't use (guix ui).
584 (error-reporting-wrapper copy-file (source target) target))
585
586 (set! canonicalize-path
587 (error-reporting-wrapper canonicalize-path (file) file))
588
589 (set! delete-file
590 (error-reporting-wrapper delete-file (file) file))
591
592 (set! execlp
593 (error-reporting-wrapper execlp (filename . args) filename))
594
595 (define (make-regexp* regexp . flags)
596 "Like 'make-regexp' but error out if REGEXP is invalid, reporting the error
597 nicely."
598 (catch 'regular-expression-syntax
599 (lambda ()
600 (apply make-regexp regexp flags))
601 (lambda (key proc message . rest)
602 (leave (G_ "'~a' is not a valid regular expression: ~a~%")
603 regexp message))))
604
605 (define (string->number* str)
606 "Like `string->number', but error out with an error message on failure."
607 (or (string->number str)
608 (leave (G_ "~a: invalid number~%") str)))
609
610 (define (size->number str)
611 "Convert STR, a storage measurement representation such as \"1024\" or
612 \"1MiB\", to a number of bytes. Raise an error if STR could not be
613 interpreted."
614 (define unit-pos
615 (string-rindex str
616 (char-set-union (char-set #\.) char-set:digit)))
617
618 (define unit
619 (and unit-pos (substring str (+ 1 unit-pos))))
620
621 (let* ((numstr (if unit-pos
622 (substring str 0 (+ 1 unit-pos))
623 str))
624 (num (string->number numstr)))
625 (unless num
626 (leave (G_ "invalid number: ~a~%") numstr))
627
628 ((compose inexact->exact round)
629 (* num
630 (match unit
631 ((or "KiB" "K" "k") (expt 2 10))
632 ((or "MiB" "M") (expt 2 20))
633 ((or "GiB" "G") (expt 2 30))
634 ((or "TiB" "T") (expt 2 40))
635 ((or "PiB" "P") (expt 2 50))
636 ((or "EiB" "E") (expt 2 60))
637 ((or "ZiB" "Z") (expt 2 70))
638 ((or "YiB" "Y") (expt 2 80))
639 ("kB" (expt 10 3))
640 ("MB" (expt 10 6))
641 ("GB" (expt 10 9))
642 ("TB" (expt 10 12))
643 ("PB" (expt 10 15))
644 ("EB" (expt 10 18))
645 ("ZB" (expt 10 21))
646 ("YB" (expt 10 24))
647 ("" 1)
648 (x
649 (leave (G_ "unknown unit: ~a~%") unit)))))))
650
651 (define (display-collision-resolution-hint collision)
652 "Display hints on how to resolve COLLISION, a &profile-collistion-error."
653 (define (top-most-entry entry)
654 (let loop ((entry entry))
655 (match (force (manifest-entry-parent entry))
656 (#f entry)
657 (parent (loop parent)))))
658
659 (let* ((first (profile-collision-error-entry collision))
660 (second (profile-collision-error-conflict collision))
661 (name1 (manifest-entry-name (top-most-entry first)))
662 (name2 (manifest-entry-name (top-most-entry second))))
663 (if (string=? name1 name2)
664 (display-hint (format #f (G_ "You cannot have two different versions
665 or variants of @code{~a} in the same profile.")
666 name1))
667 (display-hint (format #f (G_ "Try upgrading both @code{~a} and @code{~a},
668 or remove one of them from the profile.")
669 name1 name2)))))
670
671 ;; On Guile 3.0, in 'call-with-error-handling' we need to re-raise. To
672 ;; preserve useful backtraces in case of unhandled errors, we want that to
673 ;; happen before the stack has been unwound, hence 'guard*'.
674 (define-syntax-rule (guard* (var clauses ...) exp ...)
675 "This variant of SRFI-34 'guard' does not unwind the stack before
676 evaluating the tests and bodies of CLAUSES."
677 (with-exception-handler
678 (lambda (var)
679 (cond clauses ... (else (raise var))))
680 (lambda () exp ...)
681 #:unwind? #f))
682
683 (define (call-with-error-handling thunk)
684 "Call THUNK within a user-friendly error handler."
685 (define (port-filename* port)
686 ;; 'port-filename' returns #f for non-file ports, but it raises an
687 ;; exception for file ports that are closed. Work around that.
688 (and (not (port-closed? port))
689 (port-filename port)))
690
691 (guard* (c ((package-input-error? c)
692 (let* ((package (package-error-package c))
693 (input (package-error-invalid-input c))
694 (location (package-location package))
695 (file (location-file location))
696 (line (location-line location))
697 (column (location-column location)))
698 (leave (G_ "~a:~a:~a: package `~a' has an invalid input: ~s~%")
699 file line column
700 (package-full-name package) input)))
701 ((package-cross-build-system-error? c)
702 (let* ((package (package-error-package c))
703 (loc (package-location package))
704 (system (package-build-system package)))
705 (leave (G_ "~a: ~a: build system `~a' does not support cross builds~%")
706 (location->string loc)
707 (package-full-name package)
708 (build-system-name system))))
709 ((gexp-input-error? c)
710 (let ((input (gexp-error-invalid-input c)))
711 (leave (G_ "~s: invalid G-expression input~%")
712 (gexp-error-invalid-input c))))
713 ((profile-not-found-error? c)
714 (leave (G_ "profile '~a' does not exist~%")
715 (profile-error-profile c)))
716 ((missing-generation-error? c)
717 (leave (G_ "generation ~a of profile '~a' does not exist~%")
718 (missing-generation-error-generation c)
719 (profile-error-profile c)))
720 ((unmatched-pattern-error? c)
721 (let ((pattern (unmatched-pattern-error-pattern c)))
722 (leave (G_ "package '~a~@[@~a~]~@[:~a~]' not found in profile~%")
723 (manifest-pattern-name pattern)
724 (manifest-pattern-version pattern)
725 (match (manifest-pattern-output pattern)
726 ("out" #f)
727 (output output)))))
728 ((profile-collision-error? c)
729 (let ((entry (profile-collision-error-entry c))
730 (conflict (profile-collision-error-conflict c)))
731 (define (report-parent-entries entry)
732 (let ((parent (force (manifest-entry-parent entry))))
733 (when (manifest-entry? parent)
734 (report-error (G_ " ... propagated from ~a@~a~%")
735 (manifest-entry-name parent)
736 (manifest-entry-version parent))
737 (report-parent-entries parent))))
738
739 (define (manifest-entry-output* entry)
740 (match (manifest-entry-output entry)
741 ("out" "")
742 (output (string-append ":" output))))
743
744 (report-error (G_ "profile contains conflicting entries for ~a~a~%")
745 (manifest-entry-name entry)
746 (manifest-entry-output* entry))
747 (report-error (G_ " first entry: ~a@~a~a ~a~%")
748 (manifest-entry-name entry)
749 (manifest-entry-version entry)
750 (manifest-entry-output* entry)
751 (manifest-entry-item entry))
752 (report-parent-entries entry)
753 (report-error (G_ " second entry: ~a@~a~a ~a~%")
754 (manifest-entry-name conflict)
755 (manifest-entry-version conflict)
756 (manifest-entry-output* conflict)
757 (manifest-entry-item conflict))
758 (report-parent-entries conflict)
759 (display-collision-resolution-hint c)
760 (exit 1)))
761 ((nar-error? c)
762 (let ((file (nar-error-file c))
763 (port (nar-error-port c)))
764 (if file
765 (leave (G_ "corrupt input while restoring '~a' from ~s~%")
766 file (or (port-filename* port) port))
767 (leave (G_ "corrupt input while restoring archive from ~s~%")
768 (or (port-filename* port) port)))))
769 ((store-connection-error? c)
770 (leave (G_ "failed to connect to `~a': ~a~%")
771 (store-connection-error-file c)
772 (strerror (store-connection-error-code c))))
773 ((store-protocol-error? c)
774 ;; FIXME: Server-provided error messages aren't i18n'd.
775 (leave (G_ "~a~%")
776 (store-protocol-error-message c)))
777 ((derivation-missing-output-error? c)
778 (leave (G_ "reference to invalid output '~a' of derivation '~a'~%")
779 (derivation-missing-output c)
780 (derivation-file-name (derivation-error-derivation c))))
781 ((file-search-error? c)
782 (leave (G_ "file '~a' could not be found in these \
783 directories:~{ ~a~}~%")
784 (file-search-error-file-name c)
785 (file-search-error-search-path c)))
786 ((invoke-error? c)
787 (leave (G_ "program exited\
788 ~@[ with non-zero exit status ~a~]\
789 ~@[ terminated by signal ~a~]\
790 ~@[ stopped by signal ~a~]: ~s~%")
791 (invoke-error-exit-status c)
792 (invoke-error-term-signal c)
793 (invoke-error-stop-signal c)
794 (cons (invoke-error-program c)
795 (invoke-error-arguments c))))
796
797 ((formatted-message? c)
798 (apply report-error
799 (and (error-location? c) (error-location c))
800 (gettext (formatted-message-string c) %gettext-domain)
801 (formatted-message-arguments c))
802 (when (fix-hint? c)
803 (display-hint (condition-fix-hint c)))
804 (exit 1))
805
806 ;; On Guile 3.0.0, exceptions such as 'unbound-variable' are
807 ;; compound and include a '&message'. However, that message only
808 ;; contains the format string. Thus, special-case it here to
809 ;; avoid displaying a bare format string.
810 ;;
811 ;; Furthermore, use of 'guard*' ensures that the stack has not
812 ;; been unwound when we re-raise, since that would otherwise show
813 ;; useless backtraces.
814 (((exception-predicate &exception-with-kind-and-args) c)
815 (if (eq? 'system-error (exception-kind c)) ;EPIPE & co.
816 (match (exception-args c)
817 ((proc format-string format-args . _)
818 (leave (G_ "~a: ~a~%") proc
819 (apply format #f format-string format-args))))
820 (raise c)))
821
822 ((message-condition? c)
823 ;; Normally '&message' error conditions have an i18n'd message.
824 (report-error (and (error-location? c) (error-location c))
825 (G_ "~a~%")
826 (gettext (condition-message c) %gettext-domain))
827 (when (fix-hint? c)
828 (display-hint (condition-fix-hint c)))
829 (exit 1)))
830 (thunk)))
831
832 (define-syntax-rule (leave-on-EPIPE exp ...)
833 "Run EXP... in a context where EPIPE errors are caught and lead to 'exit'
834 with successful exit code. This is useful when writing to the standard output
835 may lead to EPIPE, because the standard output is piped through 'head' or
836 similar."
837 (catch 'system-error
838 (lambda ()
839 exp ...)
840 (lambda args
841 ;; We really have to exit this brutally, otherwise Guile eventually
842 ;; attempts to flush all the ports, leading to an uncaught EPIPE down
843 ;; the path.
844 (if (= EPIPE (system-error-errno args))
845 (primitive-_exit 0)
846 (apply throw args)))))
847
848 (define %guix-user-module
849 ;; Module in which user expressions are evaluated.
850 ;; Compute lazily to avoid circularity with (guix gexp).
851 (delay
852 (let ((module (make-module)))
853 (beautify-user-module! module)
854 ;; Use (guix gexp) so that one can use #~ & co.
855 (module-use! module (resolve-interface '(guix gexp)))
856 module)))
857
858 (define (read/eval str)
859 "Read and evaluate STR, raising an error if something goes wrong."
860 (let ((exp (catch #t
861 (lambda ()
862 (call-with-input-string str read))
863 (lambda args
864 (leave (G_ "failed to read expression ~s: ~s~%")
865 str args)))))
866 (catch #t
867 (lambda ()
868 (eval exp (force %guix-user-module)))
869 (lambda args
870 (report-error (G_ "failed to evaluate expression '~a':~%") exp)
871 (match args
872 (('syntax-error proc message properties form . rest)
873 (report-error (G_ "syntax error: ~a~%") message))
874 (((or 'srfi-34 '%exception) obj)
875 (cond ((message-condition? obj)
876 (report-error (G_ "~a~%")
877 (gettext (condition-message obj)
878 %gettext-domain)))
879 ((formatted-message? obj)
880 (apply report-error #f
881 (gettext (formatted-message-string obj)
882 %gettext-domain)
883 (formatted-message-arguments obj)))
884 (else
885 (report-error (G_ "exception thrown: ~s~%") obj))))
886 ((error args ...)
887 (apply display-error #f (current-error-port) args))
888 (what? #f))
889 (exit 1)))))
890
891 (define (read/eval-package-expression str)
892 "Read and evaluate STR and return the package it refers to, or exit an
893 error."
894 (match (read/eval str)
895 ((? package? p) p)
896 (x
897 (leave (G_ "expression ~s does not evaluate to a package~%")
898 str))))
899
900 (define (show-derivation-outputs derivation)
901 "Show the output file names of DERIVATION, which can be a derivation or a
902 derivation input."
903 (define (show-outputs derivation outputs)
904 (format #t "~{~a~%~}"
905 (map (cut derivation->output-path derivation <>)
906 outputs)))
907
908 (match derivation
909 ((? derivation?)
910 (show-outputs derivation (derivation-output-names derivation)))
911 ((? derivation-input? input)
912 (show-outputs (derivation-input-derivation input)
913 (derivation-input-sub-derivations input)))))
914
915 (define* (check-available-space need
916 #:optional (directory (%store-prefix)))
917 "Make sure at least NEED bytes are available in DIRECTORY. Otherwise emit a
918 warning."
919 (let ((free (catch 'system-error
920 (lambda ()
921 (free-disk-space directory))
922 (const #f))))
923 (when (and free (>= need free))
924 (warning (G_ "at least ~,1h MB needed but only ~,1h MB available in ~a~%")
925 (/ need 1e6) (/ free 1e6) directory))))
926
927 (define (graft-derivation? drv)
928 "Return true if DRV is definitely a graft derivation, false otherwise."
929 (match (assq-ref (derivation-properties drv) 'type)
930 ('graft #t)
931 (_ #f)))
932
933 (define (profile-hook-derivation? drv)
934 "Return true if DRV is definitely a profile hook derivation, false otherwise."
935 (match (assq-ref (derivation-properties drv) 'type)
936 ('profile-hook #t)
937 (_ #f)))
938
939 (define (colorize-store-file-name file)
940 "Colorize FILE, a store file name, such that the hash part is less prominent
941 that the rest."
942 (let ((len (string-length file))
943 (prefix (+ (string-length (%store-prefix)) 32 2)))
944 (if (< len prefix)
945 file
946 (string-append (colorize-string (string-take file prefix)
947 (color DARK))
948 (string-drop file prefix)))))
949
950 (define %default-verbosity
951 ;; Default verbosity level for 'show-what-to-build'.
952 2)
953
954 (define* (show-what-to-build store drv
955 #:key dry-run? (use-substitutes? #t)
956 (verbosity %default-verbosity)
957 (mode (build-mode normal)))
958 "Show what will or would (depending on DRY-RUN?) be built in realizing the
959 derivations listed in DRV using MODE, a 'build-mode' value. The elements of
960 DRV can be either derivations or derivation inputs.
961
962 Return two values: a Boolean indicating whether there's something to build,
963 and a Boolean indicating whether there's something to download.
964
965 When USE-SUBSTITUTES?, check and report what is prerequisites are available
966 for download. VERBOSITY is an integer indicating the level of details to be
967 shown: level 2 and higher provide all the details, level 1 shows a high-level
968 summary, and level 0 shows nothing."
969 (define inputs
970 (map (match-lambda
971 ((? derivation? drv) (derivation-input drv))
972 ((? derivation-input? input) input))
973 drv))
974
975 (define substitutable-info
976 ;; Call 'substitution-oracle' upfront so we don't end up launching the
977 ;; substituter many times. This makes a big difference, especially when
978 ;; DRV is a long list as is the case with 'guix environment'.
979 (if use-substitutes?
980 (substitution-oracle store inputs #:mode mode)
981 (const #f)))
982
983 (define colorized-store-item
984 (if (color-output? (current-error-port))
985 colorize-store-file-name
986 identity))
987
988 (let*-values (((build/full download)
989 (derivation-build-plan store inputs
990 #:mode mode
991 #:substitutable-info
992 substitutable-info))
993 ((graft hook build)
994 (match (fold (lambda (drv acc)
995 (let ((file (derivation-file-name drv)))
996 (match acc
997 ((#:graft graft #:hook hook #:build build)
998 (cond
999 ((graft-derivation? drv)
1000 `(#:graft ,(cons file graft)
1001 #:hook ,hook
1002 #:build ,build))
1003 ((profile-hook-derivation? drv)
1004 `(#:graft ,graft
1005 #:hook ,(cons file hook)
1006 #:build ,build))
1007 (else
1008 `(#:graft ,graft
1009 #:hook ,hook
1010 #:build ,(cons file build))))))))
1011 '(#:graft () #:hook () #:build ())
1012 build/full)
1013 ((#:graft graft #:hook hook #:build build)
1014 (values graft hook build)))))
1015 (define installed-size
1016 (reduce + 0 (map substitutable-nar-size download)))
1017
1018 (define download-size
1019 (/ (reduce + 0 (map substitutable-download-size download))
1020 1e6))
1021
1022 (define display-download-size?
1023 ;; Sometimes narinfos lack information about the download size. Only
1024 ;; display when we have information for all of DOWNLOAD.
1025 (not (any (compose zero? substitutable-download-size) download)))
1026
1027 ;; Combinatorial explosion ahead along two axes: DRY-RUN? and VERBOSITY.
1028 ;; Unfortunately, this is hardly avoidable for proper i18n.
1029 (if dry-run?
1030 (begin
1031 (unless (zero? verbosity)
1032 (format (current-error-port)
1033 (N_ "~:[The following derivation would be built:~%~{ ~a~%~}~;~]"
1034 "~:[The following derivations would be built:~%~{ ~a~%~}~;~]"
1035 (length build))
1036 (null? build) (map colorized-store-item build)))
1037 (cond ((>= verbosity 2)
1038 (if display-download-size?
1039 (format (current-error-port)
1040 ;; TRANSLATORS: "MB" is for "megabyte"; it should be
1041 ;; translated to the corresponding abbreviation.
1042 (G_ "~:[~,1h MB would be downloaded:~%~{ ~a~%~}~;~]")
1043 (null? download)
1044 download-size
1045 (map (compose colorized-store-item substitutable-path)
1046 download))
1047 (format (current-error-port)
1048 (N_ "~:[The following file would be downloaded:~%~{ ~a~%~}~;~]"
1049 "~:[The following files would be downloaded:~%~{ ~a~%~}~;~]"
1050 (length download))
1051 (null? download)
1052 (map (compose colorized-store-item substitutable-path)
1053 download)))
1054 (format (current-error-port)
1055 (N_ "~:[The following graft would be made:~%~{ ~a~%~}~;~]"
1056 "~:[The following grafts would be made:~%~{ ~a~%~}~;~]"
1057 (length graft))
1058 (null? graft) (map colorized-store-item graft))
1059 (format (current-error-port)
1060 (N_ "~:[The following profile hook would be built:~%~{ ~a~%~}~;~]"
1061 "~:[The following profile hooks would be built:~%~{ ~a~%~}~;~]"
1062 (length hook))
1063 (null? hook) (map colorized-store-item hook)))
1064 ((= verbosity 1)
1065 ;; Display the bare minimum; don't mention grafts and hooks.
1066 (unless (null? build)
1067 (newline (current-error-port)))
1068 (if display-download-size?
1069 (format (current-error-port)
1070 ;; TRANSLATORS: "MB" is for "megabyte"; it should be
1071 ;; translated to the corresponding abbreviation.
1072 (highlight (G_ "~:[~,1h MB would be downloaded~%~;~]"))
1073 (null? download) download-size)
1074 (format (current-error-port)
1075 (highlight
1076 (N_ "~:[~h item would be downloaded~%~;~]"
1077 "~:[~h items would be downloaded~%~;~]"
1078 (length download)))
1079 (null? download) (length download))))))
1080
1081 (begin
1082 (unless (zero? verbosity)
1083 (format (current-error-port)
1084 (N_ "~:[The following derivation will be built:~%~{ ~a~%~}~;~]"
1085 "~:[The following derivations will be built:~%~{ ~a~%~}~;~]"
1086 (length build))
1087 (null? build) (map colorized-store-item build)))
1088 (cond ((>= verbosity 2)
1089 (if display-download-size?
1090 (format (current-error-port)
1091 ;; TRANSLATORS: "MB" is for "megabyte"; it should be
1092 ;; translated to the corresponding abbreviation.
1093 (G_ "~:[~,1h MB will be downloaded:~%~{ ~a~%~}~;~]")
1094 (null? download)
1095 download-size
1096 (map (compose colorized-store-item substitutable-path)
1097 download))
1098 (format (current-error-port)
1099 (N_ "~:[The following file will be downloaded:~%~{ ~a~%~}~;~]"
1100 "~:[The following files will be downloaded:~%~{ ~a~%~}~;~]"
1101 (length download))
1102 (null? download)
1103 (map (compose colorized-store-item substitutable-path)
1104 download)))
1105 (format (current-error-port)
1106 (N_ "~:[The following graft will be made:~%~{ ~a~%~}~;~]"
1107 "~:[The following grafts will be made:~%~{ ~a~%~}~;~]"
1108 (length graft))
1109 (null? graft) (map colorized-store-item graft))
1110 (format (current-error-port)
1111 (N_ "~:[The following profile hook will be built:~%~{ ~a~%~}~;~]"
1112 "~:[The following profile hooks will be built:~%~{ ~a~%~}~;~]"
1113 (length hook))
1114 (null? hook) (map colorized-store-item hook)))
1115 ((= verbosity 1)
1116 ;; Display the bare minimum; don't mention grafts and hooks.
1117 (unless (null? build)
1118 (newline (current-error-port)))
1119 (if display-download-size?
1120 (format (current-error-port)
1121 ;; TRANSLATORS: "MB" is for "megabyte"; it should be
1122 ;; translated to the corresponding abbreviation.
1123 (highlight (G_ "~:[~,1h MB will be downloaded~%~;~]"))
1124 (null? download) download-size)
1125 (format (current-error-port)
1126 (highlight
1127 (N_ "~:[~h item will be downloaded~%~;~]"
1128 "~:[~h items will be downloaded~%~;~]"
1129 (length download)))
1130 (null? download) (length download)))))))
1131
1132 (check-available-space installed-size)
1133
1134 (values (pair? build/full) (pair? download))))
1135
1136 (define show-what-to-build*
1137 (store-lift show-what-to-build))
1138
1139 (define* (build-notifier #:key (dry-run? #f) (use-substitutes? #t)
1140 (verbosity %default-verbosity))
1141 "Return a procedure suitable for 'with-build-handler' that, when
1142 'build-things' is called, invokes 'show-what-to-build' to display the build
1143 plan. When DRY-RUN? is true, the 'with-build-handler' form returns without
1144 any build happening."
1145 (define not-comma
1146 (char-set-complement (char-set #\,)))
1147
1148 (define (read-derivation-from-file* item)
1149 (catch 'system-error
1150 (lambda ()
1151 (read-derivation-from-file item))
1152 (const #f)))
1153
1154 (lambda (continue store things mode)
1155 (define inputs
1156 ;; List of derivation inputs to build. Filter out non-existent '.drv'
1157 ;; files because the daemon transparently tries to substitute them.
1158 (filter-map (match-lambda
1159 (((? derivation-path? drv) . output)
1160 (let ((drv (read-derivation-from-file* drv))
1161 (outputs (string-tokenize output not-comma)))
1162 (and drv (derivation-input drv outputs))))
1163 ((? derivation-path? drv)
1164 (and=> (read-derivation-from-file* drv)
1165 derivation-input))
1166 (_
1167 #f))
1168 things))
1169
1170 (let-values (((build? download?)
1171 (show-what-to-build store inputs
1172 #:dry-run? dry-run?
1173 #:use-substitutes? use-substitutes?
1174 #:verbosity verbosity
1175 #:mode mode)))
1176
1177 (unless (and (or build? download?)
1178 dry-run?)
1179 (continue #t)))))
1180
1181 (define (right-arrow port)
1182 "Return either a string containing the 'RIGHT ARROW' character, or an ASCII
1183 replacement if PORT is not Unicode-capable."
1184 (let ((encoding (port-encoding port))
1185 (arrow "→"))
1186 (catch 'encoding-error
1187 (lambda ()
1188 (call-with-output-string
1189 (lambda (port)
1190 (set-port-encoding! port encoding)
1191 (set-port-conversion-strategy! port 'error)
1192 (display arrow port))))
1193 (lambda (key . args)
1194 "->"))))
1195
1196 (define* (tabulate rows #:key (initial-indent 0) (max-width 25)
1197 (inter-column " "))
1198 "Return a list of strings where each string is a tabulated representation of
1199 an element of ROWS. All the ROWS must be lists of the same number of cells.
1200
1201 Add INITIAL-INDENT white space at the beginning of each row. Ensure that
1202 columns are at most MAX-WIDTH characters wide. Use INTER-COLUMN as a
1203 separator between subsequent columns."
1204 (define column-widths
1205 ;; List of column widths.
1206 (let loop ((rows rows)
1207 (widths '()))
1208 (match rows
1209 (((? null?) ...)
1210 (reverse widths))
1211 (((column rest ...) ...)
1212 (loop rest
1213 (cons (min (apply max (map string-length column))
1214 max-width)
1215 widths))))))
1216
1217 (define indent
1218 (make-string initial-indent #\space))
1219
1220 (define (string-pad-right* str len)
1221 (if (> (string-length str) len)
1222 str
1223 (string-pad-right str len)))
1224
1225 (map (lambda (row)
1226 (string-trim-right
1227 (string-append indent
1228 (string-join
1229 (map string-pad-right* row column-widths)
1230 inter-column))))
1231 rows))
1232
1233 (define* (show-manifest-transaction store manifest transaction
1234 #:key dry-run?)
1235 "Display what will/would be installed/removed from MANIFEST by TRANSACTION."
1236 (define* (package-strings names versions outputs #:key old-versions)
1237 (tabulate (stable-sort
1238 (zip (map (lambda (name output)
1239 (if (string=? output "out")
1240 name
1241 (string-append name ":" output)))
1242 names outputs)
1243 (if old-versions
1244 (map (lambda (old new)
1245 (if (string=? old new)
1246 (G_ "(dependencies or package changed)")
1247 (string-append old " " → " " new)))
1248 old-versions versions)
1249 versions))
1250 (lambda (x y)
1251 (string<? (first x) (first y))))
1252 #:initial-indent 3))
1253
1254 (define → ;an arrow that can be represented on stderr
1255 (right-arrow (current-error-port)))
1256
1257 (let-values (((remove install upgrade downgrade)
1258 (manifest-transaction-effects manifest transaction)))
1259 (match remove
1260 ((($ <manifest-entry> name version output item) ..1)
1261 (let ((len (length name))
1262 (remove (package-strings name version output)))
1263 (if dry-run?
1264 (format (current-error-port)
1265 (N_ "The following package would be removed:~%~{~a~%~}~%"
1266 "The following packages would be removed:~%~{~a~%~}~%"
1267 len)
1268 remove)
1269 (format (current-error-port)
1270 (N_ "The following package will be removed:~%~{~a~%~}~%"
1271 "The following packages will be removed:~%~{~a~%~}~%"
1272 len)
1273 remove))))
1274 (x #f))
1275 (match downgrade
1276 (((($ <manifest-entry> name old-version)
1277 . ($ <manifest-entry> _ new-version output item)) ..1)
1278 (let ((len (length name))
1279 (downgrade (package-strings name new-version output
1280 #:old-versions old-version)))
1281 (if dry-run?
1282 (format (current-error-port)
1283 (N_ "The following package would be downgraded:~%~{~a~%~}~%"
1284 "The following packages would be downgraded:~%~{~a~%~}~%"
1285 len)
1286 downgrade)
1287 (format (current-error-port)
1288 (N_ "The following package will be downgraded:~%~{~a~%~}~%"
1289 "The following packages will be downgraded:~%~{~a~%~}~%"
1290 len)
1291 downgrade))))
1292 (x #f))
1293 (match upgrade
1294 (((($ <manifest-entry> name old-version)
1295 . ($ <manifest-entry> _ new-version output item)) ..1)
1296 (let ((len (length name))
1297 (upgrade (package-strings name new-version output
1298 #:old-versions old-version)))
1299 (if dry-run?
1300 (format (current-error-port)
1301 (N_ "The following package would be upgraded:~%~{~a~%~}~%"
1302 "The following packages would be upgraded:~%~{~a~%~}~%"
1303 len)
1304 upgrade)
1305 (format (current-error-port)
1306 (N_ "The following package will be upgraded:~%~{~a~%~}~%"
1307 "The following packages will be upgraded:~%~{~a~%~}~%"
1308 len)
1309 upgrade))))
1310 (x #f))
1311 (match install
1312 ((($ <manifest-entry> name version output item _) ..1)
1313 (let ((len (length name))
1314 (install (package-strings name version output)))
1315 (if dry-run?
1316 (format (current-error-port)
1317 (N_ "The following package would be installed:~%~{~a~%~}~%"
1318 "The following packages would be installed:~%~{~a~%~}~%"
1319 len)
1320 install)
1321 (format (current-error-port)
1322 (N_ "The following package will be installed:~%~{~a~%~}~%"
1323 "The following packages will be installed:~%~{~a~%~}~%"
1324 len)
1325 install))))
1326 (x #f))))
1327
1328 (define-syntax with-error-handling
1329 (syntax-rules ()
1330 "Run BODY within a user-friendly error condition handler."
1331 ((_ body ...)
1332 (call-with-error-handling
1333 (lambda ()
1334 body ...)))))
1335
1336 (define* (indented-string str indent
1337 #:key (initial-indent? #t))
1338 "Return STR with each newline preceded by INDENT spaces. When
1339 INITIAL-INDENT? is true, the first line is also indented."
1340 (define indent-string
1341 (make-list indent #\space))
1342
1343 (list->string
1344 (string-fold-right (lambda (chr result)
1345 (if (eqv? chr #\newline)
1346 (cons chr (append indent-string result))
1347 (cons chr result)))
1348 '()
1349 (if initial-indent?
1350 (string-append (list->string indent-string) str)
1351 str))))
1352
1353 (define* (fill-paragraph str width #:optional (column 0))
1354 "Fill STR such that each line contains at most WIDTH characters, assuming
1355 that the first character is at COLUMN.
1356
1357 When STR contains a single line break surrounded by other characters, it is
1358 converted to a space; sequences of more than one line break are preserved."
1359 (define (maybe-break chr result)
1360 (match result
1361 ((column newlines chars)
1362 (case chr
1363 ((#\newline)
1364 `(,column ,(+ 1 newlines) ,chars))
1365 (else
1366 (let* ((spaces (if (and (pair? chars) (eqv? (car chars) #\.)) 2 1))
1367 (chars (case newlines
1368 ((0) chars)
1369 ((1)
1370 (append (make-list spaces #\space) chars))
1371 (else
1372 (append (make-list newlines #\newline) chars))))
1373 (column (case newlines
1374 ((0) column)
1375 ((1) (+ spaces column))
1376 (else 0))))
1377 (let ((chars (cons chr chars))
1378 (column (+ 1 column)))
1379 (if (> column width)
1380 (let*-values (((before after)
1381 (break (cut eqv? #\space <>) chars))
1382 ((len)
1383 (length before)))
1384 (if (<= len width)
1385 `(,len
1386 0
1387 ,(if (null? after)
1388 before
1389 (append before
1390 (cons #\newline
1391 (drop-while (cut eqv? #\space <>)
1392 after)))))
1393 `(,column 0 ,chars))) ; unbreakable
1394 `(,column 0 ,chars)))))))))
1395
1396 (match (string-fold maybe-break
1397 `(,column 0 ())
1398 str)
1399 ((column newlines chars)
1400 (list->string (reverse chars)))))
1401
1402 \f
1403 ;;;
1404 ;;; Packages.
1405 ;;;
1406
1407 (define %text-width
1408 ;; '*line-width*' was introduced in Guile 2.2.7/3.0.1. On older versions of
1409 ;; Guile, monkey-patch 'wrap*' below.
1410 (if (defined? '*line-width*)
1411 (let ((parameter (fluid->parameter *line-width*)))
1412 (parameter (terminal-columns))
1413 parameter)
1414 (make-parameter (terminal-columns))))
1415
1416 (unless (defined? '*line-width*) ;Guile < 2.2.7
1417 (set! (@@ (texinfo plain-text) wrap*)
1418 ;; XXX: Monkey patch this private procedure to let 'package->recutils'
1419 ;; parameterize the fill of description field correctly.
1420 (lambda strings
1421 (let ((indent (fluid-ref (@@ (texinfo plain-text) *indent*))))
1422 (fill-string (string-concatenate strings)
1423 #:line-width (%text-width) #:initial-indent indent
1424 #:subsequent-indent indent)))))
1425
1426 (define (texi->plain-text str)
1427 "Return a plain-text representation of texinfo fragment STR."
1428 ;; 'texi-fragment->stexi' uses a string port so make sure it's a
1429 ;; Unicode-capable one (see <http://bugs.gnu.org/11197>.)
1430 (with-fluids ((%default-port-encoding "UTF-8"))
1431 (stexi->plain-text (texi-fragment->stexi str))))
1432
1433 (define (package-field-string package field-accessor)
1434 "Return a plain-text representation of PACKAGE field."
1435 (and=> (field-accessor package)
1436 (compose texi->plain-text P_)))
1437
1438 (define (package-description-string package)
1439 "Return a plain-text representation of PACKAGE description field."
1440 (package-field-string package package-description))
1441
1442 (define (package-synopsis-string package)
1443 "Return a plain-text representation of PACKAGE synopsis field."
1444 (package-field-string package package-synopsis))
1445
1446 (define (string->recutils str)
1447 "Return a version of STR where newlines have been replaced by newlines
1448 followed by \"+ \", which makes for a valid multi-line field value in the
1449 `recutils' syntax."
1450 (list->string
1451 (string-fold-right (lambda (chr result)
1452 (if (eqv? chr #\newline)
1453 (cons* chr #\+ #\space result)
1454 (cons chr result)))
1455 '()
1456 str)))
1457
1458 (define (hyperlink uri text)
1459 "Return a string that denotes a hyperlink using an OSC escape sequence as
1460 documented at
1461 <https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda>."
1462 (string-append "\x1b]8;;" uri "\x1b\\"
1463 text "\x1b]8;;\x1b\\"))
1464
1465 (define* (supports-hyperlinks? #:optional (port (current-output-port)))
1466 "Return true if PORT is a terminal that supports hyperlink escapes."
1467 ;; Note that terminals are supposed to ignore OSC escapes they don't
1468 ;; understand (this is the case of xterm as of version 349, for instance.)
1469 ;; However, Emacs comint as of 26.3 does not ignore it and instead lets it
1470 ;; through, hence the 'INSIDE_EMACS' special case below.
1471 (and (isatty?* port)
1472 (not (getenv "INSIDE_EMACS"))))
1473
1474 (define* (file-hyperlink file #:optional (text file))
1475 "Return TEXT with escapes for a hyperlink to FILE."
1476 (hyperlink (string-append "file://" (gethostname)
1477 (encode-and-join-uri-path
1478 (string-split file #\/)))
1479 text))
1480
1481 (define (location->hyperlink location)
1482 "Return a string corresponding to LOCATION, with escapes for a hyperlink."
1483 (let ((str (location->string location))
1484 (file (if (string-prefix? "/" (location-file location))
1485 (location-file location)
1486 (search-path %load-path (location-file location)))))
1487 (if file
1488 (file-hyperlink file str)
1489 str)))
1490
1491 (define* (package->recutils p port #:optional (width (%text-width))
1492 #:key
1493 (hyperlinks? (supports-hyperlinks? port))
1494 (extra-fields '()))
1495 "Write to PORT a `recutils' record of package P, arranging to fit within
1496 WIDTH columns. EXTRA-FIELDS is a list of symbol/value pairs to emit. When
1497 HYPERLINKS? is true, emit hyperlink escape sequences when appropriate."
1498 (define width*
1499 ;; The available number of columns once we've taken into account space for
1500 ;; the initial "+ " prefix.
1501 (if (> width 2) (- width 2) width))
1502
1503 (define (dependencies->recutils packages)
1504 (let ((list (string-join (delete-duplicates
1505 (map package-full-name
1506 (sort packages package<?))) " ")))
1507 (string->recutils
1508 (fill-paragraph list width*
1509 (string-length "dependencies: ")))))
1510
1511 (define (package<? p1 p2)
1512 (string<? (package-full-name p1) (package-full-name p2)))
1513
1514 ;; Note: Don't i18n field names so that people can post-process it.
1515 (format port "name: ~a~%" (package-name p))
1516 (format port "version: ~a~%" (package-version p))
1517 (format port "outputs: ~a~%" (string-join (package-outputs p)))
1518 (format port "systems: ~a~%"
1519 (string-join (package-transitive-supported-systems p)))
1520 (format port "dependencies: ~a~%"
1521 (match (package-direct-inputs p)
1522 (((labels inputs . _) ...)
1523 (dependencies->recutils (filter package? inputs)))))
1524 (format port "location: ~a~%"
1525 (or (and=> (package-location p)
1526 (if hyperlinks? location->hyperlink location->string))
1527 (G_ "unknown")))
1528
1529 ;; Note: Starting from version 1.6 or recutils, hyphens are not allowed in
1530 ;; field identifiers.
1531 (format port "homepage: ~a~%" (package-home-page p))
1532
1533 (format port "license: ~a~%"
1534 (match (package-license p)
1535 (((? license? licenses) ...)
1536 (string-join (map license-name licenses)
1537 ", "))
1538 ((? license? license)
1539 (let ((text (license-name license))
1540 (uri (license-uri license)))
1541 (if (and hyperlinks? uri (string-prefix? "http" uri))
1542 (hyperlink uri text)
1543 text)))
1544 (x
1545 (G_ "unknown"))))
1546 (format port "synopsis: ~a~%"
1547 (string-map (match-lambda
1548 (#\newline #\space)
1549 (chr chr))
1550 (or (package-synopsis-string p) "")))
1551 (format port "~a~%"
1552 (string->recutils
1553 (string-trim-right
1554 (parameterize ((%text-width width*))
1555 ;; Call 'texi->plain-text' on the concatenated string to account
1556 ;; for the width of "description:" in paragraph filling.
1557 (texi->plain-text
1558 (string-append "description: "
1559 (or (and=> (package-description p) P_)
1560 ""))))
1561 #\newline)))
1562 (for-each (match-lambda
1563 ((field . value)
1564 (let ((field (symbol->string field)))
1565 (format port "~a: ~a~%"
1566 field
1567 (fill-paragraph (object->string value) width*
1568 (string-length field))))))
1569 extra-fields)
1570 (newline port))
1571
1572 \f
1573 ;;;
1574 ;;; Searching.
1575 ;;;
1576
1577 (define (relevance obj regexps metrics)
1578 "Compute a \"relevance score\" for OBJ as a function of its number of
1579 matches of REGEXPS and accordingly to METRICS. METRICS is list of
1580 field/weight pairs, where FIELD is a procedure that returns a string or list
1581 of strings describing OBJ, and WEIGHT is a positive integer denoting the
1582 weight of this field in the final score.
1583
1584 A score of zero means that OBJ does not match any of REGEXPS. The higher the
1585 score, the more relevant OBJ is to REGEXPS."
1586 (define (score regexp str)
1587 (fold-matches regexp str 0
1588 (lambda (m score)
1589 (+ score
1590 (if (string=? (match:substring m) str)
1591 5 ;exact match
1592 1)))))
1593
1594 (define (regexp->score regexp)
1595 (let ((score-regexp (lambda (str) (score regexp str))))
1596 (fold (lambda (metric relevance)
1597 (match metric
1598 ((field . weight)
1599 (match (field obj)
1600 (#f relevance)
1601 ((? string? str)
1602 (+ relevance (* (score-regexp str) weight)))
1603 ((lst ...)
1604 (+ relevance (* weight (apply + (map score-regexp lst)))))))))
1605 0 metrics)))
1606
1607 (let loop ((regexps regexps)
1608 (total-score 0))
1609 (match regexps
1610 ((head . tail)
1611 (let ((score (regexp->score head)))
1612 ;; Return zero if one of PATTERNS doesn't match.
1613 (if (zero? score)
1614 0
1615 (loop tail (+ total-score score)))))
1616 (() total-score))))
1617
1618 (define %package-metrics
1619 ;; Metrics used to compute the "relevance score" of a package against a set
1620 ;; of regexps.
1621 `((,package-name . 4)
1622
1623 ;; Match against uncommon outputs.
1624 (,(lambda (package)
1625 (filter (lambda (output)
1626 (not (member output
1627 ;; Some common outputs shared by many packages.
1628 '("out" "doc" "debug" "lib" "include" "bin"))))
1629 (package-outputs package)))
1630 . 1)
1631
1632 ;; Match regexps on the raw Texinfo since formatting it is quite expensive
1633 ;; and doesn't have much of an effect on search results.
1634 (,(lambda (package)
1635 (and=> (package-synopsis package) P_)) . 3)
1636 (,(lambda (package)
1637 (and=> (package-description package) P_)) . 2)
1638
1639 (,(lambda (type)
1640 (match (and=> (package-location type) location-file)
1641 ((? string? file) (basename file ".scm"))
1642 (#f "")))
1643 . 1)))
1644
1645 (define (package-relevance package regexps)
1646 "Return a score denoting the relevance of PACKAGE for REGEXPS. A score of
1647 zero means that PACKAGE does not match any of REGEXPS."
1648 (relevance package regexps %package-metrics))
1649
1650 (define* (call-with-paginated-output-port proc
1651 #:key (less-options "FrX"))
1652 (let ((pager-command-line (or (getenv "GUIX_PAGER")
1653 (getenv "PAGER")
1654 "less")))
1655 ;; Setting PAGER to the empty string conventionally disables paging.
1656 (if (and (not (string-null? pager-command-line))
1657 (isatty?* (current-output-port)))
1658 ;; Set 'LESS' so that 'less' exits if everything fits on the screen
1659 ;; (F), lets ANSI escapes through (r), does not send the termcap
1660 ;; initialization string (X). Set it unconditionally because some
1661 ;; distros set it to something that doesn't work here.
1662 ;;
1663 ;; For things that produce long lines, such as 'guix processes', use
1664 ;; 'R' instead of 'r': this strips hyperlinks but allows 'less' to
1665 ;; make a good estimate of the line length.
1666 (let* ((pager (with-environment-variables `(("LESS" ,less-options))
1667 (apply open-pipe* OPEN_WRITE
1668 ;; Split into arguments. Treat runs of multiple
1669 ;; whitespace characters as one. libpipeline-
1670 ;; style "cmd one\ arg" escaping is unsupported.
1671 (remove (lambda (s) (string-null? s))
1672 (string-split pager-command-line
1673 char-set:whitespace))))))
1674 (dynamic-wind
1675 (const #t)
1676 (lambda () (proc pager))
1677 (lambda () (close-pipe pager))))
1678 (proc (current-output-port)))))
1679
1680 (define-syntax with-paginated-output-port
1681 (syntax-rules ()
1682 "Evaluate EXP... with PORT bound to a port that talks to the pager if
1683 standard output is a tty, or with PORT set to the current output port."
1684 ((_ port exp ... #:less-options opts)
1685 (call-with-paginated-output-port (lambda (port) exp ...)
1686 #:less-options opts))
1687 ((_ port exp ...)
1688 (call-with-paginated-output-port (lambda (port) exp ...)))))
1689
1690 (define* (display-search-results matches port
1691 #:key
1692 (command "guix search")
1693 (print package->recutils))
1694 "Display MATCHES, a list of object/score pairs, by calling PRINT on each of
1695 them. If PORT is a terminal, print at most a full screen of results."
1696 (define first-line
1697 (port-line port))
1698
1699 (define max-rows
1700 (and first-line (isatty? port)
1701 (terminal-rows port)))
1702
1703 (define (line-count str)
1704 (string-count str #\newline))
1705
1706 (with-paginated-output-port paginated
1707 (let loop ((matches matches))
1708 (match matches
1709 (((package . score) rest ...)
1710 (let* ((links? (supports-hyperlinks? port)))
1711 (print package paginated
1712 #:hyperlinks? links?
1713 #:extra-fields `((relevance . ,score)))
1714 (loop rest)))
1715 (()
1716 #t)))))
1717
1718 \f
1719 (define (string->generations str)
1720 "Return the list of generations matching a pattern in STR. This function
1721 accepts the following patterns: \"1\", \"1,2,3\", \"1..9\", \"1..\", \"..9\"."
1722 (define (maybe-integer)
1723 (let ((x (string->number str)))
1724 (and (integer? x)
1725 x)))
1726
1727 (define (maybe-comma-separated-integers)
1728 (let ((lst (delete-duplicates
1729 (map string->number
1730 (string-split str #\,)))))
1731 (and (every integer? lst)
1732 lst)))
1733
1734 (cond ((maybe-integer)
1735 =>
1736 list)
1737 ((maybe-comma-separated-integers)
1738 =>
1739 identity)
1740 ((string-match "^([0-9]+)\\.\\.([0-9]+)$" str)
1741 =>
1742 (lambda (match)
1743 (let ((s (string->number (match:substring match 1)))
1744 (e (string->number (match:substring match 2))))
1745 (and (every integer? (list s e))
1746 (<= s e)
1747 (iota (1+ (- e s)) s)))))
1748 ((string-match "^([0-9]+)\\.\\.$" str)
1749 =>
1750 (lambda (match)
1751 (let ((s (string->number (match:substring match 1))))
1752 (and (integer? s)
1753 `(>= ,s)))))
1754 ((string-match "^\\.\\.([0-9]+)$" str)
1755 =>
1756 (lambda (match)
1757 (let ((e (string->number (match:substring match 1))))
1758 (and (integer? e)
1759 `(<= ,e)))))
1760 (else #f)))
1761
1762 (define (string->duration str)
1763 "Return the duration matching a pattern in STR. This function accepts the
1764 following patterns: \"1d\", \"1w\", \"1m\"."
1765 (define (hours->duration hours match)
1766 (make-time time-duration 0
1767 (* 3600 hours (string->number (match:substring match 1)))))
1768
1769 (cond ((string-match "^([0-9]+)s$" str)
1770 =>
1771 (lambda (match)
1772 (make-time time-duration 0
1773 (string->number (match:substring match 1)))))
1774 ((string-match "^([0-9]+)h$" str)
1775 =>
1776 (lambda (match)
1777 (hours->duration 1 match)))
1778 ((string-match "^([0-9]+)d$" str)
1779 =>
1780 (lambda (match)
1781 (hours->duration 24 match)))
1782 ((string-match "^([0-9]+)w$" str)
1783 =>
1784 (lambda (match)
1785 (hours->duration (* 24 7) match)))
1786 ((string-match "^([0-9]+)m$" str)
1787 =>
1788 (lambda (match)
1789 (hours->duration (* 24 30) match)))
1790 (else #f)))
1791
1792 (define* (matching-generations str profile
1793 #:key (duration-relation <=))
1794 "Return the list of available generations matching a pattern in STR. See
1795 'string->generations' and 'string->duration' for the list of valid patterns.
1796 When STR is a duration pattern, return all the generations whose ctime has
1797 DURATION-RELATION with the current time."
1798 (define (valid-generations lst)
1799 (define (valid-generation? n)
1800 (any (cut = n <>) (generation-numbers profile)))
1801
1802 (fold-right (lambda (x acc)
1803 (if (valid-generation? x)
1804 (cons x acc)
1805 acc))
1806 '()
1807 lst))
1808
1809 (define (filter-generations generations)
1810 (match generations
1811 (() '())
1812 (('>= n)
1813 (drop-while (cut > n <>)
1814 (generation-numbers profile)))
1815 (('<= n)
1816 (valid-generations (iota n 1)))
1817 ((lst ..1)
1818 (valid-generations lst))
1819 (x #f)))
1820
1821 (define (filter-by-duration duration)
1822 (define (time-at-midnight time)
1823 ;; Return TIME at midnight by setting nanoseconds, seconds, minutes, and
1824 ;; hours to zeros.
1825 (let ((d (time-utc->date time)))
1826 (date->time-utc
1827 (make-date 0 0 0 0
1828 (date-day d) (date-month d)
1829 (date-year d) (date-zone-offset d)))))
1830
1831 (define generation-ctime-alist
1832 (map (lambda (number)
1833 (cons number
1834 (time-second
1835 (time-at-midnight
1836 (generation-time profile number)))))
1837 (generation-numbers profile)))
1838
1839 (match duration
1840 (#f #f)
1841 (res
1842 (let ((s (time-second
1843 (subtract-duration (time-at-midnight (current-time))
1844 duration))))
1845 (delete #f (map (lambda (x)
1846 (and (duration-relation s (cdr x))
1847 (first x)))
1848 generation-ctime-alist))))))
1849
1850 (cond ((string->generations str)
1851 =>
1852 filter-generations)
1853 ((string->duration str)
1854 =>
1855 filter-by-duration)
1856 (else
1857 (raise
1858 (formatted-message (G_ "invalid syntax: ~a~%") str)))))
1859
1860 (define (display-generation profile number)
1861 "Display a one-line summary of generation NUMBER of PROFILE."
1862 (unless (zero? number)
1863 (let* ((file (generation-file-name profile number))
1864 (link (if (supports-hyperlinks?)
1865 (cut file-hyperlink file <>)
1866 identity))
1867 (header (format #f (link (highlight (G_ "Generation ~a\t~a")))
1868 number
1869 (date->string
1870 (time-utc->date
1871 (generation-time profile number))
1872 ;; TRANSLATORS: This is a format-string for date->string.
1873 ;; Please choose a format that corresponds to the
1874 ;; usual way of presenting dates in your locale.
1875 ;; See https://www.gnu.org/software/guile/manual/html_node/SRFI_002d19-Date-to-string.html
1876 ;; for details.
1877 (G_ "~b ~d ~Y ~T"))))
1878 (current (generation-number profile)))
1879 (if (= number current)
1880 ;; TRANSLATORS: The word "current" here is an adjective for
1881 ;; "Generation", as in "current generation". Use the appropriate
1882 ;; gender where applicable.
1883 (format #t (G_ "~a\t(current)~%") header)
1884 (format #t "~a~%" header)))))
1885
1886 (define (display-profile-content-diff profile gen1 gen2)
1887 "Display the changed packages in PROFILE GEN2 compared to generation GEN1."
1888
1889 (define (equal-entry? first second)
1890 (string= (manifest-entry-item first) (manifest-entry-item second)))
1891
1892 (define (display-entry entry prefix)
1893 (match entry
1894 (($ <manifest-entry> name version output location _)
1895 (format #t " ~a ~a\t~a\t~a\t~a~%" prefix name version output location))))
1896
1897 (define (list-entries number)
1898 (manifest-entries (profile-manifest (generation-file-name profile number))))
1899
1900 (define (display-diff profile old new)
1901 (display-generation profile new)
1902 (let ((added (lset-difference
1903 equal-entry? (list-entries new) (list-entries old)))
1904 (removed (lset-difference
1905 equal-entry? (list-entries old) (list-entries new))))
1906 (for-each (cut display-entry <> "+") added)
1907 (for-each (cut display-entry <> "-") removed)
1908 (newline)))
1909
1910 (display-diff profile gen1 gen2))
1911
1912 (define (profile-lock-handler profile errno . _)
1913 "Handle failure to acquire PROFILE's lock."
1914 ;; NFS mounts can return ENOLCK. When that happens, there's not much that
1915 ;; can be done, so warn the user and keep going.
1916 (if (= errno ENOLCK)
1917 (warning (G_ "cannot lock profile ~a: ~a~%")
1918 profile (strerror errno))
1919 (leave (G_ "profile ~a is locked by another process~%")
1920 profile)))
1921
1922 (define profile-lock-file
1923 (cut string-append <> ".lock"))
1924
1925 (define-syntax-rule (with-profile-lock profile exp ...)
1926 "Grab PROFILE's lock and evaluate EXP... Call 'leave' if the lock is
1927 already taken."
1928 (with-file-lock/no-wait (profile-lock-file profile)
1929 (cut profile-lock-handler profile <...>)
1930 exp ...))
1931
1932 (define (display-profile-content profile number)
1933 "Display the packages in PROFILE, generation NUMBER, in a human-readable
1934 way."
1935 (for-each (match-lambda
1936 (($ <manifest-entry> name version output location _)
1937 (format #t " ~a\t~a\t~a\t~a~%"
1938 name version output location)))
1939
1940 ;; Show most recently installed packages last.
1941 (reverse
1942 (manifest-entries
1943 (profile-manifest (generation-file-name profile number))))))
1944
1945 (define (display-generation-change previous current)
1946 (format #t (G_ "switched from generation ~a to ~a~%") previous current))
1947
1948 (define (roll-back* store profile)
1949 "Like 'roll-back', but display what is happening."
1950 (call-with-values
1951 (lambda ()
1952 (roll-back store profile))
1953 display-generation-change))
1954
1955 (define (switch-to-generation* profile number)
1956 "Like 'switch-to-generation', but display what is happening."
1957 (let ((previous (switch-to-generation profile number)))
1958 (display-generation-change previous number)))
1959
1960 (define (delete-generation* store profile generation)
1961 "Like 'delete-generation', but display what is going on."
1962 (format #t (G_ "deleting ~a~%")
1963 (generation-file-name profile generation))
1964 (delete-generation store profile generation))
1965
1966 (define* (package-specification->name+version+output spec
1967 #:optional (output "out"))
1968 "Parse package specification SPEC and return three value: the specified
1969 package name, version number (or #f), and output name (or OUTPUT). SPEC may
1970 optionally contain a version number and an output name, as in these examples:
1971
1972 guile
1973 guile@2.0.9
1974 guile:debug
1975 guile@2.0.9:debug
1976 "
1977 (let*-values (((name sub-drv)
1978 (match (string-rindex spec #\:)
1979 (#f (values spec output))
1980 (colon (values (substring spec 0 colon)
1981 (substring spec (+ 1 colon))))))
1982 ((name version)
1983 (package-name->name+version name)))
1984 (values name version sub-drv)))
1985
1986 \f
1987 ;;;
1988 ;;; Command-line option processing.
1989 ;;;
1990
1991 (define (show-guix-usage)
1992 (format (current-error-port)
1993 (G_ "Try `guix --help' for more information.~%"))
1994 (exit 1))
1995
1996 ;; Representation of a 'guix' command.
1997 (define-immutable-record-type <command>
1998 (command name synopsis category)
1999 command?
2000 (name command-name)
2001 (synopsis command-synopsis)
2002 (category command-category))
2003
2004 (define (source-file-command file)
2005 "Read FILE, a Scheme source file, and return either a <command> object based
2006 on the 'define-command' top-level form found therein, or #f if FILE does not
2007 contain a 'define-command' form."
2008 (define command-name
2009 (match (filter (negate string-null?)
2010 (string-split file #\/))
2011 ((_ ... "guix" (or "scripts" "extensions") name)
2012 (list (file-sans-extension name)))
2013 ((_ ... "guix" (or "scripts" "extensions") first second)
2014 (list first (file-sans-extension second)))))
2015
2016 ;; The strategy here is to parse FILE. This is much cheaper than a
2017 ;; technique based on run-time introspection where we'd load FILE and all
2018 ;; the modules it depends on.
2019 (call-with-input-file file
2020 (lambda (port)
2021 (let loop ()
2022 (match (read port)
2023 (('define-command _ ('synopsis synopsis)
2024 _ ...)
2025 (command command-name synopsis 'main))
2026 (('define-command _
2027 ('category category) ('synopsis synopsis)
2028 _ ...)
2029 (command command-name synopsis category))
2030 ((? eof-object?)
2031 #f)
2032 (_
2033 (loop)))))))
2034
2035 (define* (command-files #:optional directory)
2036 "Return the list of source files that define Guix sub-commands."
2037 (define directory*
2038 (or directory
2039 (and=> (search-path %load-path "guix.scm")
2040 (compose (cut string-append <> "/guix/scripts")
2041 dirname))))
2042
2043 (define dot-scm?
2044 (cut string-suffix? ".scm" <>))
2045
2046 (if directory*
2047 (map (cut string-append directory* "/" <>)
2048 (scandir directory* dot-scm?))
2049 '()))
2050
2051 (define (extension-directories)
2052 "Return the list of directories containing Guix extensions."
2053 (filter file-exists?
2054 (parse-path
2055 (getenv "GUIX_EXTENSIONS_PATH"))))
2056
2057 (define (commands)
2058 "Return the list of commands, alphabetically sorted."
2059 (filter-map source-file-command
2060 (append (command-files)
2061 (append-map command-files
2062 (extension-directories)))))
2063
2064 (define (show-guix-help)
2065 (define (internal? command)
2066 (member command '("substitute" "authenticate" "offload"
2067 "perform-download")))
2068
2069 (define (display-commands commands)
2070 (let* ((names (map (lambda (command)
2071 (string-join (command-name command)))
2072 commands))
2073 (max-width (reduce max 0 (map string-length names))))
2074 (for-each (lambda (name command)
2075 (format #t " ~a ~a~%"
2076 (string-pad-right name max-width)
2077 (G_ (command-synopsis command))))
2078 names
2079 commands)))
2080
2081 (define (category-predicate category)
2082 (lambda (command)
2083 (eq? category (command-category command))))
2084
2085 (format #t (G_ "Usage: guix COMMAND ARGS...
2086 Run COMMAND with ARGS.\n"))
2087 (newline)
2088 (format #t (G_ "COMMAND must be one of the sub-commands listed below:\n"))
2089
2090 (let ((commands (commands))
2091 (categories (module-ref (resolve-interface '(guix scripts))
2092 '%command-categories)))
2093 (for-each (match-lambda
2094 (('internal . _)
2095 #t) ;hide internal commands
2096 ((category . synopsis)
2097 (let ((relevant-commands (filter (category-predicate category)
2098 commands)))
2099 ;; Only print categories that contain commands.
2100 (match relevant-commands
2101 ((one . more)
2102 (format #t "~% ~a~%" (G_ synopsis))
2103 (display-commands relevant-commands))
2104 (_ #f)))))
2105 categories))
2106 (show-bug-report-information))
2107
2108 (define (run-guix-command command . args)
2109 "Run COMMAND with the given ARGS. Report an error when COMMAND is not
2110 found."
2111 (define (command-hint guess commands)
2112 (define command-names
2113 (map (lambda (command)
2114 (match (command-name command)
2115 ((head tail ...) head)))
2116 commands))
2117 (string-closest (symbol->string guess) command-names #:threshold 3))
2118
2119 (define module
2120 ;; Check if there is a matching extension.
2121 (match (search-path (extension-directories)
2122 (format #f "~a.scm" command))
2123 (#f
2124 (catch 'misc-error
2125 (lambda ()
2126 (resolve-interface `(guix scripts ,command)))
2127 (lambda _
2128 (let ((hint (command-hint command (commands))))
2129 (format (current-error-port)
2130 (G_ "guix: ~a: command not found~%") command)
2131 (when hint
2132 (display-hint (format #f (G_ "Did you mean @code{~a}?")
2133 hint)))
2134 (show-guix-usage)))))
2135 (file
2136 (load file)
2137 (resolve-interface `(guix extensions ,command)))))
2138
2139 (let ((command-main (module-ref module
2140 (symbol-append 'guix- command))))
2141 (parameterize ((program-name command))
2142 (dynamic-wind
2143 (const #f)
2144 (lambda ()
2145 (apply command-main args))
2146 (lambda ()
2147 ;; Abuse 'exit-hook' (which is normally meant to be used by the
2148 ;; REPL) to run things like profiling hooks upon completion.
2149 (run-hook exit-hook))))))
2150
2151 (define (run-guix . args)
2152 "Run the 'guix' command defined by command line ARGS.
2153 Unlike 'guix-main', this procedure assumes that locale, i18n support,
2154 and signal handling have already been set up."
2155 (define option? (cut string-prefix? "-" <>))
2156
2157 ;; The default %LOAD-EXTENSIONS includes the empty string, which doubles the
2158 ;; number of 'stat' calls per entry in %LOAD-PATH. Shamelessly remove it.
2159 (set! %load-extensions '(".scm"))
2160
2161 ;; Disable canonicalization so we don't don't stat unreasonably.
2162 (with-fluids ((%file-port-name-canonicalization #f))
2163 (match args
2164 (()
2165 (format (current-error-port)
2166 (G_ "guix: missing command name~%"))
2167 (show-guix-usage))
2168 ((or ("-h") ("--help"))
2169 (leave-on-EPIPE (show-guix-help)))
2170 ((or ("-V") ("--version"))
2171 (show-version-and-exit "guix"))
2172 (((? option? o) args ...)
2173 (format (current-error-port)
2174 (G_ "guix: unrecognized option '~a'~%") o)
2175 (show-guix-usage))
2176 (("help" command)
2177 (apply run-guix-command (string->symbol command)
2178 '("--help")))
2179 (("help" args ...)
2180 (leave-on-EPIPE (show-guix-help)))
2181 ((command args ...)
2182 (apply run-guix-command
2183 (string->symbol command)
2184 args)))))
2185
2186 (define (guix-main arg0 . args)
2187 (initialize-guix)
2188 (apply run-guix args))
2189
2190 ;;; Local Variables:
2191 ;;; eval: (put 'guard* 'scheme-indent-function 2)
2192 ;;; End:
2193
2194 ;;; ui.scm ends here