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