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