gnu: r-igraph: Move to (gnu packages cran).
[jackhill/guix/guix.git] / guix / ui.scm
CommitLineData
233e7676 1;;; GNU Guix --- Functional package management for GNU
09edf136 2;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
cbdfa50d 3;;; Copyright © 2013, 2018 Mark H Weaver <mhw@netris.org>
98eb8cbe 4;;; Copyright © 2013 Nikita Karetnikov <nikita@karetnikov.org>
88981dd3
AK
5;;; Copyright © 2014 Cyril Roelandt <tipecaml@gmail.com>
6;;; Copyright © 2014 Cyrill Schenkel <cyrill.schenkel@gmail.com>
689db38e 7;;; Copyright © 2014, 2015, 2017 Alex Kost <alezost@gmail.com>
88981dd3 8;;; Copyright © 2015 David Thompson <davet@gnu.org>
cf5f2ad3 9;;; Copyright © 2015, 2016 Mathieu Lirzin <mthl@gnu.org>
e95ae7c2
RJ
10;;; Copyright © 2016 Roel Janssen <roel@gnu.org>
11;;; Copyright © 2016 Benz Schenk <benz.schenk@uzh.ch>
85c3fbf5 12;;; Copyright © 2018 Kyle Meyer <kyle@kyleam.com>
15cc7e6a 13;;; Copyright © 2018 Ricardo Wurmus <rekado@elephly.net>
387e6b9e
CM
14;;; Copyright © 2019 Chris Marusich <cmmarusich@gmail.com>
15;;; Copyright © 2019 Tobias Geerinckx-Rice <me@tobias.gr>
d2cdef65 16;;; Copyright © 2019 Simon Tournier <zimon.toutoune@gmail.com>
e70a8847 17;;; Copyright © 2020 Arun Isaac <arunisaac@systemreboot.net>
073c34d7 18;;;
233e7676 19;;; This file is part of GNU Guix.
073c34d7 20;;;
233e7676 21;;; GNU Guix is free software; you can redistribute it and/or modify it
073c34d7
LC
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;;;
233e7676 26;;; GNU Guix is distributed in the hope that it will be useful, but
073c34d7
LC
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
233e7676 32;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
073c34d7
LC
33
34(define-module (guix ui)
37eed374 35 #:use-module (guix i18n)
9e1e0460 36 #:use-module (guix colors)
1b5ee3bd 37 #:use-module (guix diagnostics)
3e43166f 38 #:use-module (guix gexp)
b5bfa477 39 #:use-module (guix sets)
073c34d7
LC
40 #:use-module (guix utils)
41 #:use-module (guix store)
cdd5d6f9 42 #:use-module (guix config)
073c34d7 43 #:use-module (guix packages)
5d7a8584 44 #:use-module (guix profiles)
9bb2b96a 45 #:use-module (guix derivations)
b7071bc5
LC
46 #:use-module (guix build-system)
47 #:use-module (guix serialization)
edd25a8c
LC
48 #:use-module ((guix licenses)
49 #:select (license? license-name license-uri))
dcfc6f21 50 #:use-module ((guix build syscalls)
55e1dfa4
LC
51 #:select (free-disk-space terminal-columns terminal-rows
52 with-file-lock/no-wait))
cbdfa50d 53 #:use-module ((guix build utils)
24cb66d1
LC
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.
5a2639f9
LC
59 #:hide (package-name->name+version
60 ;; Avoid "overrides core binding" warning.
61 delete))
299112d3 62 #:use-module (srfi srfi-1)
3794ce93 63 #:use-module (srfi srfi-9 gnu)
299112d3 64 #:use-module (srfi srfi-11)
2cd09108 65 #:use-module (srfi srfi-19)
073c34d7 66 #:use-module (srfi srfi-26)
2abcc97f 67 #:use-module (srfi srfi-31)
073c34d7 68 #:use-module (srfi srfi-34)
c1d52c71 69 #:use-module (srfi srfi-35)
e31ff8b8 70 #:autoload (ice-9 ftw) (scandir)
64fc89b6 71 #:use-module (ice-9 match)
9bb2b96a 72 #:use-module (ice-9 format)
2cd09108 73 #:use-module (ice-9 regex)
c39693d7 74 #:autoload (ice-9 popen) (open-pipe* close-pipe)
f44c7aac 75 #:autoload (system base compile) (compile-file)
db030303
LC
76 #:autoload (system repl repl) (start-repl)
77 #:autoload (system repl debug) (make-debug stack->vector)
7f0f38b5 78 #:autoload (web uri) (encode-and-join-uri-path)
1cd4027c
ML
79 #:use-module (texinfo)
80 #:use-module (texinfo plain-text)
81 #:use-module (texinfo string-utils)
1b5ee3bd
LC
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
7ea1432e
DT
90 make-user-module
91 load*
4ae7559f 92 warn-about-load-error
cdd5d6f9 93 show-version-and-exit
3441e164 94 show-bug-report-information
fd688c82 95 make-regexp*
969e678e 96 string->number*
1d6243cf 97 size->number
fa394eb9 98 show-derivation-outputs
07ce23e0 99 build-notifier
9bb2b96a 100 show-what-to-build
4d043ab6 101 show-what-to-build*
5d7a8584 102 show-manifest-transaction
073c34d7 103 call-with-error-handling
64fc89b6 104 with-error-handling
2d2f98ef 105 with-unbound-variable-handling
df36e629 106 leave-on-EPIPE
ac5de156 107 read/eval
eb0880e7 108 read/eval-package-expression
8120b23e 109 check-available-space
7b322d3c 110 indented-string
299112d3 111 fill-paragraph
0649321d 112 %text-width
2748ee3b 113 texi->plain-text
1cd4027c 114 package-description-string
689db38e 115 package-synopsis-string
299112d3 116 string->recutils
e49951eb 117 package->recutils
2876b989 118 package-specification->name+version+output
4311cf96 119
7f0f38b5 120 supports-hyperlinks?
2d6bd5ed 121 hyperlink
fa983b82 122 file-hyperlink
7f0f38b5
LC
123 location->hyperlink
124
1acc5e11 125 with-paginated-output-port
c7ae219e
LC
126 relevance
127 package-relevance
4311cf96
LC
128 display-search-results
129
55e1dfa4 130 with-profile-lock
2cd09108
NK
131 string->generations
132 string->duration
e49de93a 133 matching-generations
ad18c7e6
LC
134 display-generation
135 display-profile-content
e95ae7c2 136 display-profile-content-diff
06d45f45
LC
137 roll-back*
138 switch-to-generation*
139 delete-generation*
a725504a
LC
140
141 %default-message-language
142 current-message-language
143
e49951eb 144 run-guix-command
caa6732e 145 run-guix
5d9f9ad6 146 guix-main))
073c34d7
LC
147
148;;; Commentary:
149;;;
150;;; User interface facilities for command-line tools.
151;;;
152;;; Code:
153
dc856223
LC
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.
2d2f98ef 160 (format port (G_ "error: ~a: unbound variable") variable))
dc856223
LC
161 (_
162 (default-printer))))
163
164(set-exception-printer! 'unbound-variable print-unbound-variable-error)
165
7ea1432e
DT
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
7f2f6a2c
LC
175(define (last-frame-with-source stack)
176 "Walk stack upwards and return the last frame that has source location
177information, or #f if it could not be found."
2abcc97f
LC
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
abbb9871
LC
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))))
2abcc97f
LC
189 frame
190 (loop (frame-previous frame) frame)))))
191
7f2f6a2c
LC
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."
db030303
LC
201 (define (error-string frame args)
202 (call-with-output-string
f44c7aac
LC
203 (lambda (port)
204 (apply display-error frame port (cdr args)))))
db030303
LC
205
206 (define tag
207 (make-prompt-tag "user-code"))
208
7ea1432e
DT
209 (catch #t
210 (lambda ()
2abcc97f 211 ;; XXX: Force a recompilation to avoid ABI issues.
92423868
LC
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,
f44c7aac
LC
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>.
7949c140
LC
220 (unless (string=? (version) "2.2.3")
221 (set! %fresh-auto-compile #t))
92423868 222
2abcc97f 223 (set! %load-should-auto-compile #t)
7ea1432e
DT
224
225 (save-module-excursion
226 (lambda ()
227 (set-current-module user-module)
7ea1432e 228
2abcc97f
LC
229 ;; Hide the "auto-compiling" messages.
230 (parameterize ((current-warning-port (%make-void-port "w")))
db030303
LC
231 (call-with-prompt tag
232 (lambda ()
7949c140
LC
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
db030303
LC
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
694e10af
VL
241 ;; 'primitive-load', so that FILE is compiled, which then allows
242 ;; us to provide better error reporting with source line numbers.
db030303
LC
243 (load (canonicalize-path file)))
244 (const #f))))))
2abcc97f
LC
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'.
db030303 252 (let* ((stack (make-stack #t handle-error tag))
7f2f6a2c 253 (frame (last-frame-with-source stack)))
db030303
LC
254
255 (report-load-error file args frame)
256
257 (case on-error
258 ((debug)
259 (newline)
69daee23 260 (display (G_ "entering debugger; type ',bt' for a backtrace\n"))
db030303
LC
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))))))
2abcc97f 269
a2985bb1
LC
270(define (known-variable-definition variable)
271 "Search among the currently loaded modules one that defines a variable named
272VARIABLE 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
b5bfa477
LC
282 (let loop ((modules (list (resolve-module '() #f #f #:ensure #f)))
283 (suggestions '())
284 (visited (setq)))
a2985bb1
LC
285 (match modules
286 (()
287 ;; Pick the "best" suggestion.
288 (match (sort suggestions module<?)
289 (() #f)
290 ((first _ ...) first)))
291 ((head tail ...)
b5bfa477
LC
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)))))))))))
a2985bb1 305
1b5ee3bd
LC
306(define %hint-color (color BOLD CYAN))
307
935542fb
LC
308(define* (display-hint message #:optional (port (current-error-port)))
309 "Display MESSAGE, a l10n message possibly containing Texinfo markup, to
310PORT."
a7ae18b1
LC
311 (define colorize
312 (if (color-output? port)
313 (lambda (str)
2569ef9d 314 (colorize-string str %hint-color))
a7ae18b1
LC
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))
935542fb 323
2d2f98ef
LC
324(define* (report-unbound-variable-error args #:key frame)
325 "Return the given unbound-variable error, where ARGS is the list of 'throw'
326arguments."
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
a2a94b6e
LC
339(define (check-module-matches-file module file)
340 "Check whether FILE starts with 'define-module MODULE' and print a hint if
341it 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
2abcc97f 369(define* (report-load-error file args #:optional frame)
db030303 370 "Report the failure to load FILE, a user-provided Scheme file.
1151f6ae
LC
371ARGS is the list of arguments received by the 'throw' handler."
372 (match args
e465d9e1 373 (('system-error . rest)
1151f6ae 374 (let ((err (system-error-errno args)))
69daee23 375 (report-error (G_ "failed to load '~a': ~a~%") file (strerror err))))
a6e22d84
LC
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)))
47212fc7 385 (('syntax-error proc message properties form subform . rest)
1151f6ae 386 (let ((loc (source-properties->location properties)))
47212fc7
LC
387 (report-error loc (G_ "~s: ~a~%")
388 (or subform form) message)))
2d2f98ef
LC
389 (('unbound-variable _ ...)
390 (report-unbound-variable-error args #:frame frame))
7f3bbfaf 391 (((or 'srfi-34 '%exception) obj)
252a1926
LC
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)))
23735137 404 (when (fix-hint? obj)
935542fb 405 (display-hint (condition-fix-hint obj))))
70a50305 406 ((key args ...)
69daee23 407 (report-error (G_ "failed to load '~a':~%") file)
70a50305
LC
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))))))
1151f6ae 416
a2a94b6e 417(define (warn-about-load-error file module args) ;FIXME: factorize with ↑
4ae7559f
LC
418 "Report the failure to load FILE, a user-provided Scheme file, without
419exiting. ARGS is the list of arguments received by the 'throw' handler."
420 (match args
e465d9e1 421 (('system-error . rest)
4ae7559f 422 (let ((err (system-error-errno args)))
a2a94b6e 423 (warning (G_ "failed to load '~a': ~a~%") module (strerror err))))
4ae7559f
LC
424 (('syntax-error proc message properties form . rest)
425 (let ((loc (source-properties->location properties)))
40262771 426 (warning loc (G_ "~a~%") message)))
723bdb8e
LC
427 (('unbound-variable _ ...)
428 (report-unbound-variable-error args))
7f3bbfaf 429 (((or 'srfi-34 '%exception) obj)
252a1926
LC
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))))
4ae7559f 443 ((error args ...)
a2a94b6e
LC
444 (warning (G_ "failed to load '~a':~%") module)
445 (apply display-error #f (current-error-port) args)
446 (check-module-matches-file module file))))
4ae7559f 447
2d2f98ef
LC
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
466report them in a user-friendly way."
467 (call-with-unbound-variable-handling (lambda () exp ...)))
468
a725504a
LC
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.
475Return %DEFAULT-MESSAGE-LANGUAGE if that information could not be obtained. The
476result is an ISO-639-2 language code such as \"ar\", without the territory
477part."
478 (let ((locale (setlocale LC_MESSAGES)))
479 (match (string-index locale #\_)
480 (#f locale)
481 (index (string-take locale index)))))
482
b2a886f6
LC
483(define (install-locale)
484 "Install the current locale settings."
485 (catch 'system-error
486 (lambda _
487 (setlocale LC_ALL ""))
488 (lambda args
26db747a
LC
489 (display-hint (G_ "Consider installing the @code{glibc-utf8-locales} or
490@code{glibc-locales} package and defining @code{GUIX_LOCPATH}, along these
491lines:
492
493@example
494guix package -i glibc-utf8-locales
495export GUIX_LOCPATH=\"$HOME/.guix-profile/lib/locale\"
496@end example
497
fe4e770f
LC
498See 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")))))
b2a886f6 503
e49951eb 504(define (initialize-guix)
633f045f 505 "Perform the usual initialization for stand-alone Guix commands."
ce92d269
LC
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
e49951eb 513 (install-locale)
39e9f95d 514 (textdomain %gettext-domain)
e14c3929
LC
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
76832d34
LC
520 (setvbuf (current-output-port) 'line)
521 (setvbuf (current-error-port) 'line))
e49951eb 522
cdd5d6f9
LC
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)
09edf136 527 (format #t "Copyright ~a 2020 ~a"
d925cdc3
LC
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. */
69daee23
LC
531 (G_ "(C)")
532 (G_ "the Guix authors\n"))
533 (display (G_"\
64a967cc
LC
534License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
535This is free software: you are free to change and redistribute it.
536There is NO WARRANTY, to the extent permitted by law.
537"))
cdd5d6f9
LC
538 (exit 0))
539
3441e164 540(define (show-bug-report-information)
cf5f2ad3
ML
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).
69daee23 545 (format #t (G_ "
3441e164 546Report bugs to: ~a.") %guix-bug-report-address)
69daee23 547 (format #t (G_ "
3441e164 548~a home page: <~a>") %guix-package-name %guix-home-page-url)
991fdb0d
LC
549 (format #t (G_ "
550General help using Guix and GNU software: <~a>")
551 "https://guix.gnu.org/help/")
3441e164
LC
552 (newline))
553
e7ff0543
LC
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
6616a352
TS
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)
e7ff0543
LC
570 "Wrap PROC such that its 'system-error' exceptions are augmented to mention
571FILE."
572 (let ((real-proc (@ (guile) proc)))
6616a352 573 (lambda formals
e7ff0543
LC
574 (catch 'system-error
575 (lambda ()
6616a352 576 (apply-formals real-proc formals))
e7ff0543
LC
577 (augmented-system-error-handler file)))))
578
7522a016
LC
579(set! symlink
580 ;; We 'set!' the global binding because (gnu build ...) modules and similar
581 ;; typically don't use (guix ui).
e7ff0543 582 (error-reporting-wrapper symlink (source target) target))
44fd6ef1 583
9b14107f
LC
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).
e7ff0543
LC
587 (error-reporting-wrapper copy-file (source target) target))
588
6d30b1b2
LC
589(set! canonicalize-path
590 (error-reporting-wrapper canonicalize-path (file) file))
591
90b144d2
LC
592(set! delete-file
593 (error-reporting-wrapper delete-file (file) file))
594
6616a352
TS
595(set! execlp
596 (error-reporting-wrapper execlp (filename . args) filename))
9b14107f 597
fd688c82
LC
598(define (make-regexp* regexp . flags)
599 "Like 'make-regexp' but error out if REGEXP is invalid, reporting the error
600nicely."
601 (catch 'regular-expression-syntax
602 (lambda ()
603 (apply make-regexp regexp flags))
604 (lambda (key proc message . rest)
69daee23 605 (leave (G_ "'~a' is not a valid regular expression: ~a~%")
fd688c82
LC
606 regexp message))))
607
969e678e
LC
608(define (string->number* str)
609 "Like `string->number', but error out with an error message on failure."
610 (or (string->number str)
69daee23 611 (leave (G_ "~a: invalid number~%") str)))
969e678e 612
1d6243cf
LC
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
616interpreted."
617 (define unit-pos
fa99c4bb
LC
618 (string-rindex str
619 (char-set-union (char-set #\.) char-set:digit)))
1d6243cf
LC
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
69daee23 629 (leave (G_ "invalid number: ~a~%") numstr))
1d6243cf
LC
630
631 ((compose inexact->exact round)
632 (* num
633 (match unit
4a44d7bb
LC
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))
1d6243cf
LC
643 ("MB" (expt 10 6))
644 ("GB" (expt 10 9))
645 ("TB" (expt 10 12))
4a44d7bb
LC
646 ("PB" (expt 10 15))
647 ("EB" (expt 10 18))
648 ("ZB" (expt 10 21))
649 ("YB" (expt 10 24))
1d6243cf 650 ("" 1)
e465d9e1 651 (x
69daee23 652 (leave (G_ "unknown unit: ~a~%") unit)))))))
1d6243cf 653
3b80b813
LC
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
668or variants of @code{~a} in the same profile.")
669 name1))
670 (display-hint (format #f (G_ "Try upgrading both @code{~a} and @code{~a},
671or remove one of them from the profile.")
672 name1 name2)))))
673
a168c3e4
LC
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
681evaluating 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
073c34d7
LC
691(define (call-with-error-handling thunk)
692 "Call THUNK within a user-friendly error handler."
bec7f352
LC
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
a168c3e4
LC
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 \
d26e1967 791directories:~{ ~a~}~%")
a168c3e4
LC
792 (file-search-error-file-name c)
793 (file-search-error-search-path c)))
794 ((invoke-error? c)
795 (leave (G_ "program exited\
cbdfa50d
MW
796~@[ with non-zero exit status ~a~]\
797~@[ terminated by signal ~a~]\
798~@[ stopped by signal ~a~]: ~s~%")
a168c3e4
LC
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))))
a168c3e4 804
252a1926
LC
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
a168c3e4
LC
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))
05f3d340
LC
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)))
a168c3e4
LC
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))))))
073c34d7 842
df36e629 843(define-syntax-rule (leave-on-EPIPE exp ...)
694e10af 844 "Run EXP... in a context where EPIPE errors are caught and lead to 'exit'
df36e629
LC
845with successful exit code. This is useful when writing to the standard output
846may lead to EPIPE, because the standard output is piped through 'head' or
847similar."
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
56b82106
LC
859(define %guix-user-module
860 ;; Module in which user expressions are evaluated.
07254feb
LC
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)))
56b82106 868
ac5de156
LC
869(define (read/eval str)
870 "Read and evaluate STR, raising an error if something goes wrong."
eb0880e7
LC
871 (let ((exp (catch #t
872 (lambda ()
873 (call-with-input-string str read))
874 (lambda args
69daee23 875 (leave (G_ "failed to read expression ~s: ~s~%")
eb0880e7 876 str args)))))
ac5de156
LC
877 (catch #t
878 (lambda ()
07254feb 879 (eval exp (force %guix-user-module)))
ac5de156 880 (lambda args
69daee23 881 (report-error (G_ "failed to evaluate expression '~a':~%") exp)
41766807
LC
882 (match args
883 (('syntax-error proc message properties form . rest)
69daee23 884 (report-error (G_ "syntax error: ~a~%") message))
7f3bbfaf 885 (((or 'srfi-34 '%exception) obj)
252a1926
LC
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))))
41766807
LC
897 ((error args ...)
898 (apply display-error #f (current-error-port) args))
899 (what? #f))
900 (exit 1)))))
ac5de156
LC
901
902(define (read/eval-package-expression str)
903 "Read and evaluate STR and return the package it refers to, or exit an
904error."
905 (match (read/eval str)
906 ((? package? p) p)
e465d9e1 907 (x
69daee23 908 (leave (G_ "expression ~s does not evaluate to a package~%")
ac5de156 909 str))))
eb0880e7 910
fa394eb9 911(define (show-derivation-outputs derivation)
08c95ba2
LC
912 "Show the output file names of DERIVATION, which can be a derivation or a
913derivation 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)))))
fa394eb9 925
8120b23e
LC
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
dcfc6f21
LC
929warning."
930 (let ((free (catch 'system-error
931 (lambda ()
8120b23e 932 (free-disk-space directory))
dcfc6f21
LC
933 (const #f))))
934 (when (and free (>= need free))
935 (warning (G_ "at least ~,1h MB needed but only ~,1h MB available in ~a~%")
8120b23e 936 (/ need 1e6) (/ free 1e6) directory))))
dcfc6f21 937
d4aa147e
LC
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
80eebee9
RW
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
8b4615ab
LC
950(define (colorize-store-file-name file)
951 "Colorize FILE, a store file name, such that the hash part is less prominent
952that 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
a55d83b5
LC
961(define %default-verbosity
962 ;; Default verbosity level for 'show-what-to-build'.
963 2)
964
dd36b51b 965(define* (show-what-to-build store drv
58c08df0 966 #:key dry-run? (use-substitutes? #t)
a55d83b5 967 (verbosity %default-verbosity)
58c08df0 968 (mode (build-mode normal)))
9bb2b96a 969 "Show what will or would (depending on DRY-RUN?) be built in realizing the
d38d4ffa
LC
970derivations listed in DRV using MODE, a 'build-mode' value. The elements of
971DRV can be either derivations or derivation inputs.
972
883a1765 973Return two values: a Boolean indicating whether there's something to build,
a55d83b5
LC
974and a Boolean indicating whether there's something to download.
975
976When USE-SUBSTITUTES?, check and report what is prerequisites are available
977for download. VERBOSITY is an integer indicating the level of details to be
978shown: level 2 and higher provide all the details, level 1 shows a high-level
979summary, and level 0 shows nothing."
d38d4ffa
LC
980 (define inputs
981 (map (match-lambda
982 ((? derivation? drv) (derivation-input drv))
983 ((? derivation-input? input) input))
984 drv))
985
2dc98729 986 (define substitutable-info
694e10af 987 ;; Call 'substitution-oracle' upfront so we don't end up launching the
e9651e39
LC
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?
d74392a8 991 (substitution-oracle store inputs #:mode mode)
e9651e39
LC
992 (const #f)))
993
8b4615ab
LC
994 (define colorized-store-item
995 (if (color-output? (current-error-port))
996 colorize-store-file-name
997 identity))
998
bec34741 999 (let*-values (((build/full download)
d38d4ffa 1000 (derivation-build-plan store inputs
9844d009
LC
1001 #:mode mode
1002 #:substitutable-info
1003 substitutable-info))
80eebee9 1004 ((graft hook build)
9844d009
LC
1005 (match (fold (lambda (drv acc)
1006 (let ((file (derivation-file-name drv)))
80eebee9
RW
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 ())
bec34741 1023 build/full)
80eebee9
RW
1024 ((#:graft graft #:hook hook #:build build)
1025 (values graft hook build)))))
dcfc6f21
LC
1026 (define installed-size
1027 (reduce + 0 (map substitutable-nar-size download)))
1028
d9bad2f0
LC
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
a55d83b5
LC
1038 ;; Combinatorial explosion ahead along two axes: DRY-RUN? and VERBOSITY.
1039 ;; Unfortunately, this is hardly avoidable for proper i18n.
9bb2b96a 1040 (if dry-run?
dd36b51b 1041 (begin
a55d83b5
LC
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
dd36b51b 1089 (begin
a55d83b5
LC
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)))))))
dcfc6f21
LC
1136
1137 (check-available-space installed-size)
1138
883a1765 1139 (values (pair? build/full) (pair? download))))
9bb2b96a 1140
4d043ab6
DT
1141(define show-what-to-build*
1142 (store-lift show-what-to-build))
1143
a55d83b5
LC
1144(define* (build-notifier #:key (dry-run? #f) (use-substitutes? #t)
1145 (verbosity %default-verbosity))
07ce23e0
LC
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
1148plan. When DRY-RUN? is true, the 'with-build-handler' form returns without
1149any 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
9b771305
LC
1175 (let-values (((build? download?)
1176 (show-what-to-build store inputs
1177 #:dry-run? dry-run?
1178 #:use-substitutes? use-substitutes?
a55d83b5 1179 #:verbosity verbosity
9b771305
LC
1180 #:mode mode)))
1181
1182 (unless (and (or build? download?)
1183 dry-run?)
1184 (continue #t)))))
07ce23e0 1185
5d7a8584
AK
1186(define (right-arrow port)
1187 "Return either a string containing the 'RIGHT ARROW' character, or an ASCII
1188replacement if PORT is not Unicode-capable."
1062063a
LC
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 "->"))))
5d7a8584 1200
3e5ab0a7
LC
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
1204an element of ROWS. All the ROWS must be lists of the same number of cells.
1205
1206Add INITIAL-INDENT white space at the beginning of each row. Ensure that
1207columns are at most MAX-WIDTH characters wide. Use INTER-COLUMN as a
1208separator 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
5d7a8584
AK
1238(define* (show-manifest-transaction store manifest transaction
1239 #:key dry-run?)
1240 "Display what will/would be installed/removed from MANIFEST by TRANSACTION."
8465f1f6
LC
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))
5d7a8584
AK
1249
1250 (define → ;an arrow that can be represented on stderr
1251 (right-arrow (current-error-port)))
1252
3e5ab0a7
LC
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)
5c83dd1d 1261 (G_ "(dependencies or package changed)")
3e5ab0a7
LC
1262 (string-append old " " → " " new)))
1263 old-version new-version))
1264 #:initial-indent 3))
5d7a8584 1265
46b23e1a 1266 (let-values (((remove install upgrade downgrade)
5d7a8584
AK
1267 (manifest-transaction-effects manifest transaction)))
1268 (match remove
1269 ((($ <manifest-entry> name version output item) ..1)
1270 (let ((len (length name))
8465f1f6 1271 (remove (package-strings name version output)))
5d7a8584
AK
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))))
30906825 1283 (x #f))
46b23e1a
LC
1284 (match downgrade
1285 (((($ <manifest-entry> name old-version)
1286 . ($ <manifest-entry> _ new-version output item)) ..1)
1287 (let ((len (length name))
3e5ab0a7
LC
1288 (downgrade (upgrade-string name old-version new-version
1289 output)))
46b23e1a
LC
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))))
30906825 1301 (x #f))
5d7a8584
AK
1302 (match upgrade
1303 (((($ <manifest-entry> name old-version)
1304 . ($ <manifest-entry> _ new-version output item)) ..1)
1305 (let ((len (length name))
3e5ab0a7
LC
1306 (upgrade (upgrade-string name
1307 old-version new-version
1308 output)))
5d7a8584
AK
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))))
30906825 1320 (x #f))
5d7a8584
AK
1321 (match install
1322 ((($ <manifest-entry> name version output item _) ..1)
1323 (let ((len (length name))
8465f1f6 1324 (install (package-strings name version output)))
5d7a8584
AK
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))))
30906825 1336 (x #f))))
5d7a8584 1337
073c34d7
LC
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
7b322d3c
LC
1346(define* (indented-string str indent
1347 #:key (initial-indent? #t))
694e10af 1348 "Return STR with each newline preceded by INDENT spaces. When
7b322d3c
LC
1349INITIAL-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
299112d3
LC
1363(define* (fill-paragraph str width #:optional (column 0))
1364 "Fill STR such that each line contains at most WIDTH characters, assuming
1365that the first character is at COLUMN.
1366
1367When STR contains a single line break surrounded by other characters, it is
1368converted 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
3a09e1d2
CS
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))))
299112d3
LC
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
3a09e1d2
CS
1399 (append before
1400 (cons #\newline
1401 (drop-while (cut eqv? #\space <>)
1402 after)))))
299112d3
LC
1403 `(,column 0 ,chars))) ; unbreakable
1404 `(,column 0 ,chars)))))))))
1405
1406 (match (string-fold maybe-break
1407 `(,column 0 ())
1408 str)
30906825 1409 ((column newlines chars)
299112d3
LC
1410 (list->string (reverse chars)))))
1411
2876b989
LC
1412\f
1413;;;
1414;;; Packages.
1415;;;
1416
1cd4027c 1417(define %text-width
3c1ea8dc
LC
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)))))
1cd4027c
ML
1435
1436(define (texi->plain-text str)
1437 "Return a plain-text representation of texinfo fragment STR."
08d7e359
LC
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))))
1cd4027c 1442
689db38e
AK
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
1cd4027c
ML
1448(define (package-description-string package)
1449 "Return a plain-text representation of PACKAGE description field."
689db38e
AK
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))
1cd4027c 1455
299112d3
LC
1456(define (string->recutils str)
1457 "Return a version of STR where newlines have been replaced by newlines
1458followed 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
7f0f38b5
LC
1468(define (hyperlink uri text)
1469 "Return a string that denotes a hyperlink using an OSC escape sequence as
1470documented at
1471<https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda>."
1472 (string-append "\x1b]8;;" uri "\x1b\\"
1473 text "\x1b]8;;\x1b\\"))
1474
055f0525 1475(define* (supports-hyperlinks? #:optional (port (current-output-port)))
7f0f38b5
LC
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
fa983b82
LC
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
7f0f38b5
LC
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
fa983b82 1498 (file-hyperlink file str)
7f0f38b5
LC
1499 str)))
1500
4ee79f22 1501(define* (package->recutils p port #:optional (width (%text-width))
7f0f38b5
LC
1502 #:key
1503 (hyperlinks? (supports-hyperlinks? port))
1504 (extra-fields '()))
299112d3 1505 "Write to PORT a `recutils' record of package P, arranging to fit within
7f0f38b5
LC
1506WIDTH columns. EXTRA-FIELDS is a list of symbol/value pairs to emit. When
1507HYPERLINKS? is true, emit hyperlink escape sequences when appropriate."
069d43a7
LC
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
20ffce82 1513 (define (dependencies->recutils packages)
dbde386e
SB
1514 (let ((list (string-join (delete-duplicates
1515 (map package-full-name
1516 (sort packages package<?))) " ")))
20ffce82 1517 (string->recutils
069d43a7 1518 (fill-paragraph list width*
20ffce82
LC
1519 (string-length "dependencies: ")))))
1520
9c0fc279
CR
1521 (define (package<? p1 p2)
1522 (string<? (package-full-name p1) (package-full-name p2)))
1523
299112d3
LC
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))
6bf99bac 1527 (format port "outputs: ~a~%" (string-join (package-outputs p)))
5763ad92
LC
1528 (format port "systems: ~a~%"
1529 (string-join (package-transitive-supported-systems p)))
9c0fc279
CR
1530 (format port "dependencies: ~a~%"
1531 (match (package-direct-inputs p)
5e6feee6
EB
1532 (((labels inputs . _) ...)
1533 (dependencies->recutils (filter package? inputs)))))
299112d3 1534 (format port "location: ~a~%"
7f0f38b5
LC
1535 (or (and=> (package-location p)
1536 (if hyperlinks? location->hyperlink location->string))
69daee23 1537 (G_ "unknown")))
8fa3e6b3
LC
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
299112d3
LC
1543 (format port "license: ~a~%"
1544 (match (package-license p)
1545 (((? license? licenses) ...)
1546 (string-join (map license-name licenses)
1547 ", "))
1548 ((? license? license)
edd25a8c
LC
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)))
299112d3 1554 (x
69daee23 1555 (G_ "unknown"))))
299112d3
LC
1556 (format port "synopsis: ~a~%"
1557 (string-map (match-lambda
4ee79f22
LC
1558 (#\newline #\space)
1559 (chr chr))
a1da0904 1560 (or (package-synopsis-string p) "")))
4ee79f22 1561 (format port "~a~%"
1cd4027c
ML
1562 (string->recutils
1563 (string-trim-right
069d43a7 1564 (parameterize ((%text-width width*))
31522dfa
LC
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 ""))))
4ee79f22
LC
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))
299112d3 1581
4311cf96
LC
1582\f
1583;;;
1584;;; Searching.
1585;;;
1586
c7ae219e
LC
1587(define (relevance obj regexps metrics)
1588 "Compute a \"relevance score\" for OBJ as a function of its number of
1589matches of REGEXPS and accordingly to METRICS. METRICS is list of
387e6b9e
CM
1590field/weight pairs, where FIELD is a procedure that returns a string or list
1591of strings describing OBJ, and WEIGHT is a positive integer denoting the
1592weight of this field in the final score.
c7ae219e
LC
1593
1594A score of zero means that OBJ does not match any of REGEXPS. The higher the
1595score, the more relevant OBJ is to REGEXPS."
d2cdef65 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
e70a8847
AI
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))))
c7ae219e
LC
1627
1628(define %package-metrics
1629 ;; Metrics used to compute the "relevance score" of a package against a set
1630 ;; of regexps.
304a53f6 1631 `((,package-name . 4)
94aeec0a 1632
387e6b9e
CM
1633 ;; Match against uncommon outputs.
1634 (,(lambda (package)
1635 (filter (lambda (output)
1636 (not (member output
694e10af 1637 ;; Some common outputs shared by many packages.
387e6b9e
CM
1638 '("out" "doc" "debug" "lib" "include" "bin"))))
1639 (package-outputs package)))
1640 . 1)
1641
94aeec0a
LC
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
304a53f6
LC
1649 (,(lambda (type)
1650 (match (and=> (package-location type) location-file)
1651 ((? string? file) (basename file ".scm"))
1652 (#f "")))
1653 . 1)))
c7ae219e
LC
1654
1655(define (package-relevance package regexps)
1656 "Return a score denoting the relevance of PACKAGE for REGEXPS. A score of
1657zero means that PACKAGE does not match any of REGEXPS."
1658 (relevance package regexps %package-metrics))
1659
9296a2e5
LC
1660(define* (call-with-paginated-output-port proc
1661 #:key (less-options "FrX"))
c39693d7
LC
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
e66f243e
LC
1665 ;; initialization string (X). Set it unconditionally because some
1666 ;; distros set it to something that doesn't work here.
9296a2e5
LC
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))
c39693d7
LC
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
9296a2e5
LC
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
c39693d7 1684standard output is a tty, or with PORT set to the current output port."
9296a2e5
LC
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 ...)))))
c39693d7 1690
4311cf96
LC
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
1696them. 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
c39693d7
LC
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)))))
4311cf96
LC
1718
1719\f
2cd09108
NK
1720(define (string->generations str)
1721 "Return the list of generations matching a pattern in STR. This function
1722accepts 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
1765following 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
638c5b79
LC
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)
30d2397f 1776 =>
638c5b79
LC
1777 (lambda (match)
1778 (hours->duration 1 match)))
1779 ((string-match "^([0-9]+)d$" str)
2cd09108
NK
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
e49de93a
LC
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.
1797When STR is a duration pattern, return all the generations whose ctime has
1798DURATION-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))
30906825 1820 (x #f)))
e49de93a
LC
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)
5c3d4430
LC
1857 (else
1858 (raise
d51bfe24 1859 (formatted-message (G_ "invalid syntax: ~a~%") str)))))
e49de93a 1860
ad18c7e6
LC
1861(define (display-generation profile number)
1862 "Display a one-line summary of generation NUMBER of PROFILE."
1863 (unless (zero? number)
055f0525
LC
1864 (let* ((file (generation-file-name profile number))
1865 (link (if (supports-hyperlinks?)
1866 (cut file-hyperlink file <>)
c2f9ea2b 1867 identity))
055f0525
LC
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)))
ad18c7e6 1880 (if (= number current)
3bd9672c
LC
1881 ;; TRANSLATORS: The word "current" here is an adjective for
1882 ;; "Generation", as in "current generation". Use the appropriate
1883 ;; gender where applicable.
69daee23 1884 (format #t (G_ "~a\t(current)~%") header)
ad18c7e6
LC
1885 (format #t "~a~%" header)))))
1886
e95ae7c2 1887(define (display-profile-content-diff profile gen1 gen2)
dc013aff 1888 "Display the changed packages in PROFILE GEN2 compared to generation GEN1."
e95ae7c2
RJ
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)
88bdbb2a
RJ
1908 (for-each (cut display-entry <> "-") removed)
1909 (newline)))
e95ae7c2
RJ
1910
1911 (display-diff profile gen1 gen2))
1912
55e1dfa4
LC
1913(define (profile-lock-handler profile errno . _)
1914 "Handle failure to acquire PROFILE's lock."
3f8e890d
LC
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)))
55e1dfa4
LC
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
1928already taken."
1929 (with-file-lock/no-wait (profile-lock-file profile)
1930 (cut profile-lock-handler profile <...>)
1931 exp ...))
1932
ad18c7e6
LC
1933(define (display-profile-content profile number)
1934 "Display the packages in PROFILE, generation NUMBER, in a human-readable
1935way."
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
06d45f45 1946(define (display-generation-change previous current)
69daee23 1947 (format #t (G_ "switched from generation ~a to ~a~%") previous current))
06d45f45
LC
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."
69daee23 1963 (format #t (G_ "deleting ~a~%")
06d45f45
LC
1964 (generation-file-name profile generation))
1965 (delete-generation store profile generation))
1966
2876b989
LC
1967(define* (package-specification->name+version+output spec
1968 #:optional (output "out"))
1969 "Parse package specification SPEC and return three value: the specified
1970package name, version number (or #f), and output name (or OUTPUT). SPEC may
1971optionally contain a version number and an output name, as in these examples:
1972
1973 guile
1b846da8 1974 guile@2.0.9
2876b989 1975 guile:debug
1b846da8 1976 guile@2.0.9:debug
2876b989
LC
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
e49951eb 1992(define (show-guix-usage)
e49951eb 1993 (format (current-error-port)
69daee23 1994 (G_ "Try `guix --help' for more information.~%"))
25c93676 1995 (exit 1))
e49951eb 1996
3794ce93
LC
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
2007on the 'define-command' top-level form found therein, or #f if FILE does not
2008contain 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
e31ff8b8
LC
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
2b8cf44f
LC
2042 (define dot-scm?
2043 (cut string-suffix? ".scm" <>))
2044
e31ff8b8 2045 (if directory
3794ce93
LC
2046 (map (cut string-append directory "/" <>)
2047 (scandir directory dot-scm?))
e31ff8b8
LC
2048 '()))
2049
2050(define (commands)
3794ce93
LC
2051 "Return the list of commands, alphabetically sorted."
2052 (filter-map source-file-command (command-files)))
e31ff8b8
LC
2053
2054(define (show-guix-help)
59f734f3 2055 (define (internal? command)
94d92c77
LC
2056 (member command '("substitute" "authenticate" "offload"
2057 "perform-download")))
59f734f3 2058
3794ce93
LC
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
69daee23 2075 (format #t (G_ "Usage: guix COMMAND ARGS...
e31ff8b8
LC
2076Run COMMAND with ARGS.\n"))
2077 (newline)
69daee23 2078 (format #t (G_ "COMMAND must be one of the sub-commands listed below:\n"))
3794ce93
LC
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))
e31ff8b8
LC
2091 (show-bug-report-information))
2092
ec5d0a85
LC
2093(define (run-guix-command command . args)
2094 "Run COMMAND with the given ARGS. Report an error when COMMAND is not
2095found."
2096 (define module
2097 (catch 'misc-error
2098 (lambda ()
2099 (resolve-interface `(guix scripts ,command)))
2100 (lambda -
25c93676 2101 (format (current-error-port)
69daee23 2102 (G_ "guix: ~a: command not found~%") command)
25c93676 2103 (show-guix-usage))))
ec5d0a85
LC
2104
2105 (let ((command-main (module-ref module
2106 (symbol-append 'guix- command))))
2107 (parameterize ((program-name command))
14d5ca2e
LC
2108 ;; Disable canonicalization so we don't don't stat unreasonably.
2109 (with-fluids ((%file-port-name-canonicalization #f))
f4453df9
LC
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)))))))
ec5d0a85 2118
caa6732e
AK
2119(define (run-guix . args)
2120 "Run the 'guix' command defined by command line ARGS.
2121Unlike 'guix-main', this procedure assumes that locale, i18n support,
694e10af 2122and signal handling have already been set up."
caa6732e
AK
2123 (define option? (cut string-prefix? "-" <>))
2124
cbee9559
LC
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
caa6732e
AK
2129 (match args
2130 (()
2131 (format (current-error-port)
69daee23 2132 (G_ "guix: missing command name~%"))
caa6732e
AK
2133 (show-guix-usage))
2134 ((or ("-h") ("--help"))
2135 (show-guix-help))
85c3fbf5 2136 ((or ("-V") ("--version"))
caa6732e
AK
2137 (show-version-and-exit "guix"))
2138 (((? option? o) args ...)
2139 (format (current-error-port)
69daee23 2140 (G_ "guix: unrecognized option '~a'~%") o)
caa6732e 2141 (show-guix-usage))
2ab5fdc4
LC
2142 (("help" command)
2143 (apply run-guix-command (string->symbol command)
2144 '("--help")))
caa6732e
AK
2145 (("help" args ...)
2146 (show-guix-help))
2147 ((command args ...)
2148 (apply run-guix-command
2149 (string->symbol command)
2150 args))))
2151
e49951eb
MW
2152(define (guix-main arg0 . args)
2153 (initialize-guix)
caa6732e 2154 (apply run-guix args))
e49951eb 2155
a168c3e4
LC
2156;;; Local Variables:
2157;;; eval: (put 'guard* 'scheme-indent-function 2)
2158;;; End:
2159
073c34d7 2160;;; ui.scm ends here