ui: Factorize error-reporting wrapper code.
[jackhill/guix/guix.git] / guix / ui.scm
CommitLineData
233e7676 1;;; GNU Guix --- Functional package management for GNU
74d862e8 2;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017 Ludovic Courtès <ludo@gnu.org>
e49951eb 3;;; Copyright © 2013 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>
caa6732e 7;;; Copyright © 2014, 2015 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>
073c34d7 12;;;
233e7676 13;;; This file is part of GNU Guix.
073c34d7 14;;;
233e7676 15;;; GNU Guix is free software; you can redistribute it and/or modify it
073c34d7
LC
16;;; under the terms of the GNU General Public License as published by
17;;; the Free Software Foundation; either version 3 of the License, or (at
18;;; your option) any later version.
19;;;
233e7676 20;;; GNU Guix is distributed in the hope that it will be useful, but
073c34d7
LC
21;;; WITHOUT ANY WARRANTY; without even the implied warranty of
22;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23;;; GNU General Public License for more details.
24;;;
25;;; You should have received a copy of the GNU General Public License
233e7676 26;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
073c34d7
LC
27
28(define-module (guix ui)
29 #:use-module (guix utils)
30 #:use-module (guix store)
cdd5d6f9 31 #:use-module (guix config)
073c34d7 32 #:use-module (guix packages)
5d7a8584 33 #:use-module (guix profiles)
9bb2b96a 34 #:use-module (guix derivations)
958dd3ce 35 #:use-module (guix combinators)
b7071bc5
LC
36 #:use-module (guix build-system)
37 #:use-module (guix serialization)
59758816 38 #:use-module ((guix build utils) #:select (mkdir-p))
299112d3 39 #:use-module ((guix licenses) #:select (license? license-name))
9703fef4 40 #:use-module ((guix build syscalls) #:select (terminal-columns))
299112d3
LC
41 #:use-module (srfi srfi-1)
42 #:use-module (srfi srfi-11)
2cd09108 43 #:use-module (srfi srfi-19)
073c34d7 44 #:use-module (srfi srfi-26)
2abcc97f 45 #:use-module (srfi srfi-31)
073c34d7 46 #:use-module (srfi srfi-34)
c1d52c71 47 #:use-module (srfi srfi-35)
e31ff8b8 48 #:autoload (ice-9 ftw) (scandir)
64fc89b6 49 #:use-module (ice-9 match)
9bb2b96a 50 #:use-module (ice-9 format)
2cd09108 51 #:use-module (ice-9 regex)
db030303
LC
52 #:autoload (system repl repl) (start-repl)
53 #:autoload (system repl debug) (make-debug stack->vector)
1cd4027c
ML
54 #:use-module (texinfo)
55 #:use-module (texinfo plain-text)
56 #:use-module (texinfo string-utils)
073c34d7
LC
57 #:export (_
58 N_
ee764179 59 P_
70e629f5 60 report-error
073c34d7 61 leave
7ea1432e
DT
62 make-user-module
63 load*
4ae7559f 64 warn-about-load-error
cdd5d6f9 65 show-version-and-exit
3441e164 66 show-bug-report-information
fd688c82 67 make-regexp*
969e678e 68 string->number*
1d6243cf 69 size->number
fa394eb9 70 show-derivation-outputs
9bb2b96a 71 show-what-to-build
4d043ab6 72 show-what-to-build*
5d7a8584 73 show-manifest-transaction
073c34d7 74 call-with-error-handling
64fc89b6 75 with-error-handling
df36e629 76 leave-on-EPIPE
ac5de156 77 read/eval
eb0880e7 78 read/eval-package-expression
299112d3 79 location->string
f651b477 80 config-directory
299112d3 81 fill-paragraph
2748ee3b 82 texi->plain-text
1cd4027c 83 package-description-string
299112d3 84 string->recutils
e49951eb 85 package->recutils
2876b989 86 package-specification->name+version+output
2cd09108
NK
87 string->generations
88 string->duration
e49de93a 89 matching-generations
ad18c7e6
LC
90 display-generation
91 display-profile-content
e95ae7c2 92 display-profile-content-diff
06d45f45
LC
93 roll-back*
94 switch-to-generation*
95 delete-generation*
e49951eb 96 run-guix-command
caa6732e 97 run-guix
a2011be5
LC
98 program-name
99 guix-warning-port
100 warning
240b57f0 101 info
e49951eb 102 guix-main))
073c34d7
LC
103
104;;; Commentary:
105;;;
106;;; User interface facilities for command-line tools.
107;;;
108;;; Code:
109
110(define %gettext-domain
ee764179 111 ;; Text domain for strings used in the tools.
073c34d7
LC
112 "guix")
113
ee764179
LC
114(define %package-text-domain
115 ;; Text domain for package synopses and descriptions.
116 "guix-packages")
117
073c34d7
LC
118(define _ (cut gettext <> %gettext-domain))
119(define N_ (cut ngettext <> <> <> %gettext-domain))
a5c0d8bc
LC
120
121(define (P_ msgid)
122 "Return the translation of the package description or synopsis MSGID."
123 ;; Descriptions/synopses might occasionally be empty strings, even if that
124 ;; is something we try to avoid. Since (gettext "") can return a non-empty
125 ;; string, explicitly check for that case.
126 (if (string-null? msgid)
127 msgid
128 (gettext msgid %package-text-domain)))
073c34d7 129
b2a886f6
LC
130(define-syntax-rule (define-diagnostic name prefix)
131 "Create a diagnostic macro (i.e., NAME), which will prepend PREFIX to all
132messages."
133 (define-syntax name
134 (lambda (x)
135 (define (augmented-format-string fmt)
136 (string-append "~:[~*~;guix ~a: ~]~a" (syntax->datum fmt)))
137
89d02b98
LC
138 (syntax-case x ()
139 ((name (underscore fmt) args (... ...))
140 (and (string? (syntax->datum #'fmt))
141 (free-identifier=? #'underscore #'_))
b2a886f6
LC
142 (with-syntax ((fmt* (augmented-format-string #'fmt))
143 (prefix (datum->syntax x prefix)))
144 #'(format (guix-warning-port) (gettext fmt*)
145 (program-name) (program-name) prefix
146 args (... ...))))
89d02b98 147 ((name (N-underscore singular plural n) args (... ...))
b2a886f6 148 (and (string? (syntax->datum #'singular))
89d02b98
LC
149 (string? (syntax->datum #'plural))
150 (free-identifier=? #'N-underscore #'N_))
b2a886f6
LC
151 (with-syntax ((s (augmented-format-string #'singular))
152 (p (augmented-format-string #'plural))
153 (prefix (datum->syntax x prefix)))
154 #'(format (guix-warning-port)
155 (ngettext s p n %gettext-domain)
156 (program-name) (program-name) prefix
157 args (... ...))))))))
158
159(define-diagnostic warning "warning: ") ; emit a warning
240b57f0 160(define-diagnostic info "")
b2a886f6
LC
161
162(define-diagnostic report-error "error: ")
163(define-syntax-rule (leave args ...)
164 "Emit an error message and exit."
165 (begin
166 (report-error args ...)
167 (exit 1)))
168
7ea1432e
DT
169(define (make-user-module modules)
170 "Return a new user module with the additional MODULES loaded."
171 ;; Module in which the machine description file is loaded.
172 (let ((module (make-fresh-user-module)))
173 (for-each (lambda (iface)
174 (module-use! module (resolve-interface iface)))
175 modules)
176 module))
177
db030303
LC
178(define* (load* file user-module
179 #:key (on-error 'nothing-special))
7ea1432e 180 "Load the user provided Scheme source code FILE."
2abcc97f
LC
181 (define (frame-with-source frame)
182 ;; Walk from FRAME upwards until source location information is found.
183 (let loop ((frame frame)
184 (previous frame))
185 (if (not frame)
186 previous
187 (if (frame-source frame)
188 frame
189 (loop (frame-previous frame) frame)))))
190
db030303
LC
191 (define (error-string frame args)
192 (call-with-output-string
193 (lambda (port)
194 (apply display-error frame port (cdr args)))))
195
196 (define tag
197 (make-prompt-tag "user-code"))
198
7ea1432e
DT
199 (catch #t
200 (lambda ()
2abcc97f 201 ;; XXX: Force a recompilation to avoid ABI issues.
7ea1432e 202 (set! %fresh-auto-compile #t)
2abcc97f 203 (set! %load-should-auto-compile #t)
7ea1432e
DT
204
205 (save-module-excursion
206 (lambda ()
207 (set-current-module user-module)
7ea1432e 208
2abcc97f
LC
209 ;; Hide the "auto-compiling" messages.
210 (parameterize ((current-warning-port (%make-void-port "w")))
db030303
LC
211 (call-with-prompt tag
212 (lambda ()
213 ;; Give 'load' an absolute file name so that it doesn't try to
214 ;; search for FILE in %LOAD-PATH. Note: use 'load', not
215 ;; 'primitive-load', so that FILE is compiled, which then allows us
216 ;; to provide better error reporting with source line numbers.
217 (load (canonicalize-path file)))
218 (const #f))))))
2abcc97f
LC
219 (lambda _
220 ;; XXX: Errors are reported from the pre-unwind handler below, but
221 ;; calling 'exit' from there has no effect, so we call it here.
222 (exit 1))
223 (rec (handle-error . args)
224 ;; Capture the stack up to this procedure call, excluded, and pass
225 ;; the faulty stack frame to 'report-load-error'.
db030303 226 (let* ((stack (make-stack #t handle-error tag))
2abcc97f
LC
227 (depth (stack-length stack))
228 (last (and (> depth 0) (stack-ref stack 0)))
229 (frame (frame-with-source
230 (if (> depth 1)
231 (stack-ref stack 1) ;skip the 'throw' frame
232 last))))
db030303
LC
233
234 (report-load-error file args frame)
235
236 (case on-error
237 ((debug)
238 (newline)
239 (display (_ "entering debugger; type ',bt' for a backtrace\n"))
240 (start-repl #:debug (make-debug (stack->vector stack) 0
241 (error-string frame args)
242 #f)))
243 ((backtrace)
244 (newline (current-error-port))
245 (display-backtrace stack (current-error-port)))
246 (else
247 #t))))))
2abcc97f
LC
248
249(define* (report-load-error file args #:optional frame)
db030303 250 "Report the failure to load FILE, a user-provided Scheme file.
1151f6ae
LC
251ARGS is the list of arguments received by the 'throw' handler."
252 (match args
e465d9e1 253 (('system-error . rest)
1151f6ae 254 (let ((err (system-error-errno args)))
db030303 255 (report-error (_ "failed to load '~a': ~a~%") file (strerror err))))
1151f6ae
LC
256 (('syntax-error proc message properties form . rest)
257 (let ((loc (source-properties->location properties)))
258 (format (current-error-port) (_ "~a: error: ~a~%")
db030303 259 (location->string loc) message)))
23185cea
LC
260 (('srfi-34 obj)
261 (report-error (_ "exception thrown: ~s~%") obj))
1151f6ae
LC
262 ((error args ...)
263 (report-error (_ "failed to load '~a':~%") file)
db030303 264 (apply display-error frame (current-error-port) args))))
1151f6ae 265
4ae7559f
LC
266(define (warn-about-load-error file args) ;FIXME: factorize with ↑
267 "Report the failure to load FILE, a user-provided Scheme file, without
268exiting. ARGS is the list of arguments received by the 'throw' handler."
269 (match args
e465d9e1 270 (('system-error . rest)
4ae7559f
LC
271 (let ((err (system-error-errno args)))
272 (warning (_ "failed to load '~a': ~a~%") file (strerror err))))
273 (('syntax-error proc message properties form . rest)
274 (let ((loc (source-properties->location properties)))
275 (format (current-error-port) (_ "~a: warning: ~a~%")
276 (location->string loc) message)))
23185cea
LC
277 (('srfi-34 obj)
278 (warning (_ "failed to load '~a': exception thrown: ~s~%")
279 file obj))
4ae7559f
LC
280 ((error args ...)
281 (warning (_ "failed to load '~a':~%") file)
282 (apply display-error #f (current-error-port) args))))
283
b2a886f6
LC
284(define (install-locale)
285 "Install the current locale settings."
286 (catch 'system-error
287 (lambda _
288 (setlocale LC_ALL ""))
289 (lambda args
290 (warning (_ "failed to install locale: ~a~%")
291 (strerror (system-error-errno args))))))
292
e49951eb 293(define (initialize-guix)
633f045f 294 "Perform the usual initialization for stand-alone Guix commands."
e49951eb 295 (install-locale)
39e9f95d 296 (textdomain %gettext-domain)
e14c3929
LC
297
298 ;; Ignore SIGPIPE. If the daemon closes the connection, we prefer to be
299 ;; notified via an EPIPE later.
300 (sigaction SIGPIPE SIG_IGN)
301
e49951eb
MW
302 (setvbuf (current-output-port) _IOLBF)
303 (setvbuf (current-error-port) _IOLBF))
304
cdd5d6f9
LC
305(define* (show-version-and-exit #:optional (command (car (command-line))))
306 "Display version information for COMMAND and `(exit 0)'."
307 (simple-format #t "~a (~a) ~a~%"
308 command %guix-package-name %guix-version)
d925cdc3
LC
309 (format #t "Copyright ~a 2017 ~a"
310 ;; TRANSLATORS: Translate "(C)" to the copyright symbol
311 ;; (C-in-a-circle), if this symbol is available in the user's
312 ;; locale. Otherwise, do not translate "(C)"; leave it as-is. */
313 (_ "(C)")
314 (_ "the Guix authors\n"))
315 (display (_"\
64a967cc
LC
316License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
317This is free software: you are free to change and redistribute it.
318There is NO WARRANTY, to the extent permitted by law.
319"))
cdd5d6f9
LC
320 (exit 0))
321
3441e164 322(define (show-bug-report-information)
cf5f2ad3
ML
323 ;; TRANSLATORS: The placeholder indicates the bug-reporting address for this
324 ;; package. Please add another line saying "Report translation bugs to
325 ;; ...\n" with the address for translation bugs (typically your translation
326 ;; team's web or email address).
3441e164
LC
327 (format #t (_ "
328Report bugs to: ~a.") %guix-bug-report-address)
329 (format #t (_ "
330~a home page: <~a>") %guix-package-name %guix-home-page-url)
331 (display (_ "
332General help using GNU software: <http://www.gnu.org/gethelp/>"))
333 (newline))
334
e7ff0543
LC
335(define (augmented-system-error-handler file)
336 "Return a 'system-error' handler that mentions FILE in its message."
337 (lambda (key proc fmt args errno)
338 ;; Augment the FMT and ARGS with information about TARGET (this
339 ;; information is missing as of Guile 2.0.11, making the exception
340 ;; uninformative.)
341 (apply throw key proc "~A: ~S"
342 (list (strerror (car errno)) file)
343 (list errno))))
344
345(define-syntax-rule (error-reporting-wrapper proc (args ...) file)
346 "Wrap PROC such that its 'system-error' exceptions are augmented to mention
347FILE."
348 (let ((real-proc (@ (guile) proc)))
349 (lambda (args ...)
350 (catch 'system-error
351 (lambda ()
352 (real-proc args ...))
353 (augmented-system-error-handler file)))))
354
7522a016
LC
355(set! symlink
356 ;; We 'set!' the global binding because (gnu build ...) modules and similar
357 ;; typically don't use (guix ui).
e7ff0543 358 (error-reporting-wrapper symlink (source target) target))
44fd6ef1 359
9b14107f
LC
360(set! copy-file
361 ;; Note: here we use 'set!', not #:replace, because UIs typically use
362 ;; 'copy-recursively', which doesn't use (guix ui).
e7ff0543
LC
363 (error-reporting-wrapper copy-file (source target) target))
364
9b14107f 365
fd688c82
LC
366(define (make-regexp* regexp . flags)
367 "Like 'make-regexp' but error out if REGEXP is invalid, reporting the error
368nicely."
369 (catch 'regular-expression-syntax
370 (lambda ()
371 (apply make-regexp regexp flags))
372 (lambda (key proc message . rest)
373 (leave (_ "'~a' is not a valid regular expression: ~a~%")
374 regexp message))))
375
969e678e
LC
376(define (string->number* str)
377 "Like `string->number', but error out with an error message on failure."
378 (or (string->number str)
379 (leave (_ "~a: invalid number~%") str)))
380
1d6243cf
LC
381(define (size->number str)
382 "Convert STR, a storage measurement representation such as \"1024\" or
383\"1MiB\", to a number of bytes. Raise an error if STR could not be
384interpreted."
385 (define unit-pos
386 (string-rindex str char-set:digit))
387
388 (define unit
389 (and unit-pos (substring str (+ 1 unit-pos))))
390
391 (let* ((numstr (if unit-pos
392 (substring str 0 (+ 1 unit-pos))
393 str))
394 (num (string->number numstr)))
395 (unless num
396 (leave (_ "invalid number: ~a~%") numstr))
397
398 ((compose inexact->exact round)
399 (* num
400 (match unit
4a44d7bb
LC
401 ((or "KiB" "K" "k") (expt 2 10))
402 ((or "MiB" "M") (expt 2 20))
403 ((or "GiB" "G") (expt 2 30))
404 ((or "TiB" "T") (expt 2 40))
405 ((or "PiB" "P") (expt 2 50))
406 ((or "EiB" "E") (expt 2 60))
407 ((or "ZiB" "Z") (expt 2 70))
408 ((or "YiB" "Y") (expt 2 80))
409 ("kB" (expt 10 3))
1d6243cf
LC
410 ("MB" (expt 10 6))
411 ("GB" (expt 10 9))
412 ("TB" (expt 10 12))
4a44d7bb
LC
413 ("PB" (expt 10 15))
414 ("EB" (expt 10 18))
415 ("ZB" (expt 10 21))
416 ("YB" (expt 10 24))
1d6243cf 417 ("" 1)
e465d9e1 418 (x
1d6243cf
LC
419 (leave (_ "unknown unit: ~a~%") unit)))))))
420
073c34d7
LC
421(define (call-with-error-handling thunk)
422 "Call THUNK within a user-friendly error handler."
bec7f352
LC
423 (define (port-filename* port)
424 ;; 'port-filename' returns #f for non-file ports, but it raises an
425 ;; exception for file ports that are closed. Work around that.
426 (and (not (port-closed? port))
427 (port-filename port)))
428
073c34d7
LC
429 (guard (c ((package-input-error? c)
430 (let* ((package (package-error-package c))
431 (input (package-error-invalid-input c))
432 (location (package-location package))
433 (file (location-file location))
434 (line (location-line location))
435 (column (location-column location)))
98eb8cbe 436 (leave (_ "~a:~a:~a: package `~a' has an invalid input: ~s~%")
073c34d7
LC
437 file line column
438 (package-full-name package) input)))
9b222abe
LC
439 ((package-cross-build-system-error? c)
440 (let* ((package (package-error-package c))
441 (loc (package-location package))
442 (system (package-build-system package)))
443 (leave (_ "~a: ~a: build system `~a' does not support cross builds~%")
444 (location->string loc)
445 (package-full-name package)
446 (build-system-name system))))
c0c018f1
AK
447 ((profile-not-found-error? c)
448 (leave (_ "profile '~a' does not exist~%")
449 (profile-error-profile c)))
450 ((missing-generation-error? c)
451 (leave (_ "generation ~a of profile '~a' does not exist~%")
452 (missing-generation-error-generation c)
453 (profile-error-profile c)))
b7071bc5
LC
454 ((nar-error? c)
455 (let ((file (nar-error-file c))
456 (port (nar-error-port c)))
457 (if file
458 (leave (_ "corrupt input while restoring '~a' from ~s~%")
bec7f352 459 file (or (port-filename* port) port))
b7071bc5 460 (leave (_ "corrupt input while restoring archive from ~s~%")
bec7f352 461 (or (port-filename* port) port)))))
ef86c39f 462 ((nix-connection-error? c)
98eb8cbe 463 (leave (_ "failed to connect to `~a': ~a~%")
ef86c39f
LC
464 (nix-connection-error-file c)
465 (strerror (nix-connection-error-code c))))
073c34d7
LC
466 ((nix-protocol-error? c)
467 ;; FIXME: Server-provided error messages aren't i18n'd.
98eb8cbe 468 (leave (_ "build failed: ~a~%")
c1d52c71 469 (nix-protocol-error-message c)))
f304c9c2
LC
470 ((derivation-missing-output-error? c)
471 (leave (_ "reference to invalid output '~a' of derivation '~a'~%")
472 (derivation-missing-output c)
473 (derivation-file-name (derivation-error-derivation c))))
d26e1967
LC
474 ((file-search-error? c)
475 (leave (_ "file '~a' could not be found in these \
476directories:~{ ~a~}~%")
477 (file-search-error-file-name c)
478 (file-search-error-search-path c)))
c1d52c71
LC
479 ((message-condition? c)
480 ;; Normally '&message' error conditions have an i18n'd message.
12703d08
LC
481 (leave (_ "~a~%")
482 (gettext (condition-message c) %gettext-domain))))
e14c3929
LC
483 ;; Catch EPIPE and the likes.
484 (catch 'system-error
485 thunk
bde8c0e6
LC
486 (lambda (key proc format-string format-args . rest)
487 (leave (_ "~a: ~a~%") proc
488 (apply format #f format-string format-args))))))
073c34d7 489
df36e629
LC
490(define-syntax-rule (leave-on-EPIPE exp ...)
491 "Run EXP... in a context when EPIPE errors are caught and lead to 'exit'
492with successful exit code. This is useful when writing to the standard output
493may lead to EPIPE, because the standard output is piped through 'head' or
494similar."
495 (catch 'system-error
496 (lambda ()
497 exp ...)
498 (lambda args
499 ;; We really have to exit this brutally, otherwise Guile eventually
500 ;; attempts to flush all the ports, leading to an uncaught EPIPE down
501 ;; the path.
502 (if (= EPIPE (system-error-errno args))
503 (primitive-_exit 0)
504 (apply throw args)))))
505
56b82106
LC
506(define %guix-user-module
507 ;; Module in which user expressions are evaluated.
07254feb
LC
508 ;; Compute lazily to avoid circularity with (guix gexp).
509 (delay
510 (let ((module (make-module)))
511 (beautify-user-module! module)
512 ;; Use (guix gexp) so that one can use #~ & co.
513 (module-use! module (resolve-interface '(guix gexp)))
514 module)))
56b82106 515
ac5de156
LC
516(define (read/eval str)
517 "Read and evaluate STR, raising an error if something goes wrong."
eb0880e7
LC
518 (let ((exp (catch #t
519 (lambda ()
520 (call-with-input-string str read))
521 (lambda args
522 (leave (_ "failed to read expression ~s: ~s~%")
523 str args)))))
ac5de156
LC
524 (catch #t
525 (lambda ()
07254feb 526 (eval exp (force %guix-user-module)))
ac5de156 527 (lambda args
41766807
LC
528 (report-error (_ "failed to evaluate expression '~a':~%") exp)
529 (match args
530 (('syntax-error proc message properties form . rest)
531 (report-error (_ "syntax error: ~a~%") message))
23185cea
LC
532 (('srfi-34 obj)
533 (report-error (_ "exception thrown: ~s~%") obj))
41766807
LC
534 ((error args ...)
535 (apply display-error #f (current-error-port) args))
536 (what? #f))
537 (exit 1)))))
ac5de156
LC
538
539(define (read/eval-package-expression str)
540 "Read and evaluate STR and return the package it refers to, or exit an
541error."
542 (match (read/eval str)
543 ((? package? p) p)
e465d9e1 544 (x
ac5de156
LC
545 (leave (_ "expression ~s does not evaluate to a package~%")
546 str))))
eb0880e7 547
fa394eb9
AK
548(define (show-derivation-outputs derivation)
549 "Show the output file names of DERIVATION."
550 (format #t "~{~a~%~}"
551 (map (match-lambda
552 ((out-name . out)
553 (derivation->output-path derivation out-name)))
554 (derivation-outputs derivation))))
555
dd36b51b 556(define* (show-what-to-build store drv
58c08df0
LC
557 #:key dry-run? (use-substitutes? #t)
558 (mode (build-mode normal)))
9bb2b96a 559 "Show what will or would (depending on DRY-RUN?) be built in realizing the
58c08df0
LC
560derivations listed in DRV using MODE, a 'build-mode' value. Return #t if
561there's something to build, #f otherwise. When USE-SUBSTITUTES?, check and
562report what is prerequisites are available for download."
e9651e39
LC
563 (define substitutable?
564 ;; Call 'substitutation-oracle' upfront so we don't end up launching the
565 ;; substituter many times. This makes a big difference, especially when
566 ;; DRV is a long list as is the case with 'guix environment'.
567 (if use-substitutes?
58c08df0 568 (substitution-oracle store drv #:mode mode)
e9651e39
LC
569 (const #f)))
570
52ddf2ae 571 (define (built-or-substitutable? drv)
f304c9c2
LC
572 (or (null? (derivation-outputs drv))
573 (let ((out (derivation->output-path drv))) ;XXX: assume "out" exists
52ddf2ae 574 (or (valid-path? store out)
e9651e39 575 (substitutable? out)))))
52ddf2ae 576
dd36b51b 577 (let*-values (((build download)
59688fc4
LC
578 (fold2 (lambda (drv build download)
579 (let-values (((b d)
580 (derivation-prerequisites-to-build
581 store drv
58c08df0 582 #:mode mode
e9651e39 583 #:substitutable? substitutable?)))
59688fc4
LC
584 (values (append b build)
585 (append d download))))
dd36b51b
LC
586 '() '()
587 drv))
588 ((build) ; add the DRV themselves
589 (delete-duplicates
59688fc4 590 (append (map derivation-file-name
52ddf2ae 591 (remove built-or-substitutable? drv))
dd36b51b
LC
592 (map derivation-input-path build))))
593 ((download) ; add the references of DOWNLOAD
1a8b7834
LC
594 (if use-substitutes?
595 (delete-duplicates
596 (append download
597 (remove (cut valid-path? store <>)
598 (append-map
599 substitutable-references
600 (substitutable-path-info store
601 download)))))
602 download)))
00554b2a 603 ;; TODO: Show the installed size of DOWNLOAD.
9bb2b96a 604 (if dry-run?
dd36b51b
LC
605 (begin
606 (format (current-error-port)
83e61a73
LC
607 (N_ "~:[The following derivation would be built:~%~{ ~a~%~}~;~]"
608 "~:[The following derivations would be built:~%~{ ~a~%~}~;~]"
dd36b51b
LC
609 (length build))
610 (null? build) build)
611 (format (current-error-port)
83e61a73
LC
612 (N_ "~:[The following file would be downloaded:~%~{ ~a~%~}~;~]"
613 "~:[The following files would be downloaded:~%~{ ~a~%~}~;~]"
dd36b51b
LC
614 (length download))
615 (null? download) download))
616 (begin
617 (format (current-error-port)
83e61a73
LC
618 (N_ "~:[The following derivation will be built:~%~{ ~a~%~}~;~]"
619 "~:[The following derivations will be built:~%~{ ~a~%~}~;~]"
dd36b51b
LC
620 (length build))
621 (null? build) build)
622 (format (current-error-port)
83e61a73
LC
623 (N_ "~:[The following file will be downloaded:~%~{ ~a~%~}~;~]"
624 "~:[The following files will be downloaded:~%~{ ~a~%~}~;~]"
dd36b51b
LC
625 (length download))
626 (null? download) download)))
627 (pair? build)))
9bb2b96a 628
4d043ab6
DT
629(define show-what-to-build*
630 (store-lift show-what-to-build))
631
5d7a8584
AK
632(define (right-arrow port)
633 "Return either a string containing the 'RIGHT ARROW' character, or an ASCII
634replacement if PORT is not Unicode-capable."
635 (with-fluids ((%default-port-encoding (port-encoding port)))
636 (let ((arrow "→"))
637 (catch 'encoding-error
638 (lambda ()
639 (call-with-output-string
640 (lambda (port)
641 (set-port-conversion-strategy! port 'error)
642 (display arrow port))))
643 (lambda (key . args)
644 "->")))))
645
646(define* (show-manifest-transaction store manifest transaction
647 #:key dry-run?)
648 "Display what will/would be installed/removed from MANIFEST by TRANSACTION."
649 (define (package-strings name version output item)
650 (map (lambda (name version output item)
651 (format #f " ~a~:[:~a~;~*~]\t~a\t~a"
652 name
653 (equal? output "out") output version
654 (if (package? item)
655 (package-output store item output)
656 item)))
657 name version output item))
658
659 (define → ;an arrow that can be represented on stderr
660 (right-arrow (current-error-port)))
661
662 (define (upgrade-string name old-version new-version output item)
663 (format #f " ~a~:[:~a~;~*~]\t~a ~a ~a\t~a"
664 name (equal? output "out") output
665 old-version → new-version
666 (if (package? item)
667 (package-output store item output)
668 item)))
669
46b23e1a 670 (let-values (((remove install upgrade downgrade)
5d7a8584
AK
671 (manifest-transaction-effects manifest transaction)))
672 (match remove
673 ((($ <manifest-entry> name version output item) ..1)
674 (let ((len (length name))
675 (remove (package-strings name version output item)))
676 (if dry-run?
677 (format (current-error-port)
678 (N_ "The following package would be removed:~%~{~a~%~}~%"
679 "The following packages would be removed:~%~{~a~%~}~%"
680 len)
681 remove)
682 (format (current-error-port)
683 (N_ "The following package will be removed:~%~{~a~%~}~%"
684 "The following packages will be removed:~%~{~a~%~}~%"
685 len)
686 remove))))
687 (_ #f))
46b23e1a
LC
688 (match downgrade
689 (((($ <manifest-entry> name old-version)
690 . ($ <manifest-entry> _ new-version output item)) ..1)
691 (let ((len (length name))
692 (downgrade (map upgrade-string
693 name old-version new-version output item)))
694 (if dry-run?
695 (format (current-error-port)
696 (N_ "The following package would be downgraded:~%~{~a~%~}~%"
697 "The following packages would be downgraded:~%~{~a~%~}~%"
698 len)
699 downgrade)
700 (format (current-error-port)
701 (N_ "The following package will be downgraded:~%~{~a~%~}~%"
702 "The following packages will be downgraded:~%~{~a~%~}~%"
703 len)
704 downgrade))))
705 (_ #f))
5d7a8584
AK
706 (match upgrade
707 (((($ <manifest-entry> name old-version)
708 . ($ <manifest-entry> _ new-version output item)) ..1)
709 (let ((len (length name))
710 (upgrade (map upgrade-string
711 name old-version new-version output item)))
712 (if dry-run?
713 (format (current-error-port)
714 (N_ "The following package would be upgraded:~%~{~a~%~}~%"
715 "The following packages would be upgraded:~%~{~a~%~}~%"
716 len)
717 upgrade)
718 (format (current-error-port)
719 (N_ "The following package will be upgraded:~%~{~a~%~}~%"
720 "The following packages will be upgraded:~%~{~a~%~}~%"
721 len)
722 upgrade))))
723 (_ #f))
724 (match install
725 ((($ <manifest-entry> name version output item _) ..1)
726 (let ((len (length name))
727 (install (package-strings name version output item)))
728 (if dry-run?
729 (format (current-error-port)
730 (N_ "The following package would be installed:~%~{~a~%~}~%"
731 "The following packages would be installed:~%~{~a~%~}~%"
732 len)
733 install)
734 (format (current-error-port)
735 (N_ "The following package will be installed:~%~{~a~%~}~%"
736 "The following packages will be installed:~%~{~a~%~}~%"
737 len)
738 install))))
739 (_ #f))))
740
073c34d7
LC
741(define-syntax with-error-handling
742 (syntax-rules ()
743 "Run BODY within a user-friendly error condition handler."
744 ((_ body ...)
745 (call-with-error-handling
746 (lambda ()
747 body ...)))))
748
64fc89b6
LC
749(define (location->string loc)
750 "Return a human-friendly, GNU-standard representation of LOC."
751 (match loc
752 (#f (_ "<unknown location>"))
753 (($ <location> file line column)
754 (format #f "~a:~a:~a" file line column))))
755
f651b477
LC
756(define (config-directory)
757 "Return the name of the configuration directory, after making sure that it
758exists. Honor the XDG specs,
759<http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>."
760 (let ((dir (and=> (or (getenv "XDG_CONFIG_HOME")
761 (and=> (getenv "HOME")
762 (cut string-append <> "/.config")))
763 (cut string-append <> "/guix"))))
764 (catch 'system-error
765 (lambda ()
59758816 766 (mkdir-p dir)
f651b477
LC
767 dir)
768 (lambda args
59758816
LC
769 (let ((err (system-error-errno args)))
770 ;; ERR is necessarily different from EEXIST.
771 (leave (_ "failed to create configuration directory `~a': ~a~%")
772 dir (strerror err)))))))
f651b477 773
299112d3
LC
774(define* (fill-paragraph str width #:optional (column 0))
775 "Fill STR such that each line contains at most WIDTH characters, assuming
776that the first character is at COLUMN.
777
778When STR contains a single line break surrounded by other characters, it is
779converted to a space; sequences of more than one line break are preserved."
780 (define (maybe-break chr result)
781 (match result
782 ((column newlines chars)
783 (case chr
784 ((#\newline)
785 `(,column ,(+ 1 newlines) ,chars))
786 (else
3a09e1d2
CS
787 (let* ((spaces (if (and (pair? chars) (eqv? (car chars) #\.)) 2 1))
788 (chars (case newlines
789 ((0) chars)
790 ((1)
791 (append (make-list spaces #\space) chars))
792 (else
793 (append (make-list newlines #\newline) chars))))
794 (column (case newlines
795 ((0) column)
796 ((1) (+ spaces column))
797 (else 0))))
299112d3
LC
798 (let ((chars (cons chr chars))
799 (column (+ 1 column)))
800 (if (> column width)
801 (let*-values (((before after)
802 (break (cut eqv? #\space <>) chars))
803 ((len)
804 (length before)))
805 (if (<= len width)
806 `(,len
807 0
808 ,(if (null? after)
809 before
3a09e1d2
CS
810 (append before
811 (cons #\newline
812 (drop-while (cut eqv? #\space <>)
813 after)))))
299112d3
LC
814 `(,column 0 ,chars))) ; unbreakable
815 `(,column 0 ,chars)))))))))
816
817 (match (string-fold maybe-break
818 `(,column 0 ())
819 str)
820 ((_ _ chars)
821 (list->string (reverse chars)))))
822
2876b989
LC
823\f
824;;;
825;;; Packages.
826;;;
827
1cd4027c 828(define %text-width
9703fef4 829 (make-parameter (terminal-columns)))
1cd4027c
ML
830
831(set! (@@ (texinfo plain-text) wrap*)
832 ;; XXX: Monkey patch this private procedure to let 'package->recutils'
833 ;; parameterize the fill of description field correctly.
834 (lambda strings
835 (let ((indent (fluid-ref (@@ (texinfo plain-text) *indent*))))
836 (fill-string (string-concatenate strings)
837 #:line-width (%text-width) #:initial-indent indent
838 #:subsequent-indent indent))))
839
840(define (texi->plain-text str)
841 "Return a plain-text representation of texinfo fragment STR."
08d7e359
LC
842 ;; 'texi-fragment->stexi' uses a string port so make sure it's a
843 ;; Unicode-capable one (see <http://bugs.gnu.org/11197>.)
844 (with-fluids ((%default-port-encoding "UTF-8"))
845 (stexi->plain-text (texi-fragment->stexi str))))
1cd4027c
ML
846
847(define (package-description-string package)
848 "Return a plain-text representation of PACKAGE description field."
849 (and=> (package-description package)
850 (compose texi->plain-text P_)))
851
299112d3
LC
852(define (string->recutils str)
853 "Return a version of STR where newlines have been replaced by newlines
854followed by \"+ \", which makes for a valid multi-line field value in the
855`recutils' syntax."
856 (list->string
857 (string-fold-right (lambda (chr result)
858 (if (eqv? chr #\newline)
859 (cons* chr #\+ #\space result)
860 (cons chr result)))
861 '()
862 str)))
863
1cd4027c 864(define* (package->recutils p port #:optional (width (%text-width)))
299112d3
LC
865 "Write to PORT a `recutils' record of package P, arranging to fit within
866WIDTH columns."
069d43a7
LC
867 (define width*
868 ;; The available number of columns once we've taken into account space for
869 ;; the initial "+ " prefix.
870 (if (> width 2) (- width 2) width))
871
20ffce82
LC
872 (define (dependencies->recutils packages)
873 (let ((list (string-join (map package-full-name
874 (sort packages package<?)) " ")))
875 (string->recutils
069d43a7 876 (fill-paragraph list width*
20ffce82
LC
877 (string-length "dependencies: ")))))
878
9c0fc279
CR
879 (define (package<? p1 p2)
880 (string<? (package-full-name p1) (package-full-name p2)))
881
299112d3
LC
882 ;; Note: Don't i18n field names so that people can post-process it.
883 (format port "name: ~a~%" (package-name p))
884 (format port "version: ~a~%" (package-version p))
6bf99bac 885 (format port "outputs: ~a~%" (string-join (package-outputs p)))
5763ad92
LC
886 (format port "systems: ~a~%"
887 (string-join (package-transitive-supported-systems p)))
9c0fc279
CR
888 (format port "dependencies: ~a~%"
889 (match (package-direct-inputs p)
5e6feee6
EB
890 (((labels inputs . _) ...)
891 (dependencies->recutils (filter package? inputs)))))
299112d3
LC
892 (format port "location: ~a~%"
893 (or (and=> (package-location p) location->string)
894 (_ "unknown")))
8fa3e6b3
LC
895
896 ;; Note: Starting from version 1.6 or recutils, hyphens are not allowed in
897 ;; field identifiers.
898 (format port "homepage: ~a~%" (package-home-page p))
899
299112d3
LC
900 (format port "license: ~a~%"
901 (match (package-license p)
902 (((? license? licenses) ...)
903 (string-join (map license-name licenses)
904 ", "))
905 ((? license? license)
906 (license-name license))
907 (x
908 (_ "unknown"))))
909 (format port "synopsis: ~a~%"
910 (string-map (match-lambda
911 (#\newline #\space)
912 (chr chr))
ee764179 913 (or (and=> (package-synopsis p) P_)
299112d3 914 "")))
1cd4027c
ML
915 (format port "~a~2%"
916 (string->recutils
917 (string-trim-right
069d43a7 918 (parameterize ((%text-width width*))
1cd4027c
ML
919 (texi->plain-text
920 (string-append "description: "
921 (or (and=> (package-description p) P_)
922 ""))))
923 #\newline))))
299112d3 924
2cd09108
NK
925(define (string->generations str)
926 "Return the list of generations matching a pattern in STR. This function
927accepts the following patterns: \"1\", \"1,2,3\", \"1..9\", \"1..\", \"..9\"."
928 (define (maybe-integer)
929 (let ((x (string->number str)))
930 (and (integer? x)
931 x)))
932
933 (define (maybe-comma-separated-integers)
934 (let ((lst (delete-duplicates
935 (map string->number
936 (string-split str #\,)))))
937 (and (every integer? lst)
938 lst)))
939
940 (cond ((maybe-integer)
941 =>
942 list)
943 ((maybe-comma-separated-integers)
944 =>
945 identity)
946 ((string-match "^([0-9]+)\\.\\.([0-9]+)$" str)
947 =>
948 (lambda (match)
949 (let ((s (string->number (match:substring match 1)))
950 (e (string->number (match:substring match 2))))
951 (and (every integer? (list s e))
952 (<= s e)
953 (iota (1+ (- e s)) s)))))
954 ((string-match "^([0-9]+)\\.\\.$" str)
955 =>
956 (lambda (match)
957 (let ((s (string->number (match:substring match 1))))
958 (and (integer? s)
959 `(>= ,s)))))
960 ((string-match "^\\.\\.([0-9]+)$" str)
961 =>
962 (lambda (match)
963 (let ((e (string->number (match:substring match 1))))
964 (and (integer? e)
965 `(<= ,e)))))
966 (else #f)))
967
968(define (string->duration str)
969 "Return the duration matching a pattern in STR. This function accepts the
970following patterns: \"1d\", \"1w\", \"1m\"."
971 (define (hours->duration hours match)
972 (make-time time-duration 0
973 (* 3600 hours (string->number (match:substring match 1)))))
974
638c5b79
LC
975 (cond ((string-match "^([0-9]+)s$" str)
976 =>
977 (lambda (match)
978 (make-time time-duration 0
979 (string->number (match:substring match 1)))))
980 ((string-match "^([0-9]+)h$" str)
981 (lambda (match)
982 (hours->duration 1 match)))
983 ((string-match "^([0-9]+)d$" str)
2cd09108
NK
984 =>
985 (lambda (match)
986 (hours->duration 24 match)))
987 ((string-match "^([0-9]+)w$" str)
988 =>
989 (lambda (match)
990 (hours->duration (* 24 7) match)))
991 ((string-match "^([0-9]+)m$" str)
992 =>
993 (lambda (match)
994 (hours->duration (* 24 30) match)))
995 (else #f)))
996
e49de93a
LC
997(define* (matching-generations str profile
998 #:key (duration-relation <=))
999 "Return the list of available generations matching a pattern in STR. See
1000'string->generations' and 'string->duration' for the list of valid patterns.
1001When STR is a duration pattern, return all the generations whose ctime has
1002DURATION-RELATION with the current time."
1003 (define (valid-generations lst)
1004 (define (valid-generation? n)
1005 (any (cut = n <>) (generation-numbers profile)))
1006
1007 (fold-right (lambda (x acc)
1008 (if (valid-generation? x)
1009 (cons x acc)
1010 acc))
1011 '()
1012 lst))
1013
1014 (define (filter-generations generations)
1015 (match generations
1016 (() '())
1017 (('>= n)
1018 (drop-while (cut > n <>)
1019 (generation-numbers profile)))
1020 (('<= n)
1021 (valid-generations (iota n 1)))
1022 ((lst ..1)
1023 (valid-generations lst))
1024 (_ #f)))
1025
1026 (define (filter-by-duration duration)
1027 (define (time-at-midnight time)
1028 ;; Return TIME at midnight by setting nanoseconds, seconds, minutes, and
1029 ;; hours to zeros.
1030 (let ((d (time-utc->date time)))
1031 (date->time-utc
1032 (make-date 0 0 0 0
1033 (date-day d) (date-month d)
1034 (date-year d) (date-zone-offset d)))))
1035
1036 (define generation-ctime-alist
1037 (map (lambda (number)
1038 (cons number
1039 (time-second
1040 (time-at-midnight
1041 (generation-time profile number)))))
1042 (generation-numbers profile)))
1043
1044 (match duration
1045 (#f #f)
1046 (res
1047 (let ((s (time-second
1048 (subtract-duration (time-at-midnight (current-time))
1049 duration))))
1050 (delete #f (map (lambda (x)
1051 (and (duration-relation s (cdr x))
1052 (first x)))
1053 generation-ctime-alist))))))
1054
1055 (cond ((string->generations str)
1056 =>
1057 filter-generations)
1058 ((string->duration str)
1059 =>
1060 filter-by-duration)
1061 (else #f)))
1062
ad18c7e6
LC
1063(define (display-generation profile number)
1064 "Display a one-line summary of generation NUMBER of PROFILE."
1065 (unless (zero? number)
1066 (let ((header (format #f (_ "Generation ~a\t~a") number
1067 (date->string
1068 (time-utc->date
1069 (generation-time profile number))
1070 "~b ~d ~Y ~T")))
1071 (current (generation-number profile)))
1072 (if (= number current)
3bd9672c
LC
1073 ;; TRANSLATORS: The word "current" here is an adjective for
1074 ;; "Generation", as in "current generation". Use the appropriate
1075 ;; gender where applicable.
ad18c7e6
LC
1076 (format #t (_ "~a\t(current)~%") header)
1077 (format #t "~a~%" header)))))
1078
e95ae7c2
RJ
1079(define (display-profile-content-diff profile gen1 gen2)
1080 "Display the changed packages in PROFILE GEN2 compared to generation GEN2."
1081
1082 (define (equal-entry? first second)
1083 (string= (manifest-entry-item first) (manifest-entry-item second)))
1084
1085 (define (display-entry entry prefix)
1086 (match entry
1087 (($ <manifest-entry> name version output location _)
1088 (format #t " ~a ~a\t~a\t~a\t~a~%" prefix name version output location))))
1089
1090 (define (list-entries number)
1091 (manifest-entries (profile-manifest (generation-file-name profile number))))
1092
1093 (define (display-diff profile old new)
1094 (display-generation profile new)
1095 (let ((added (lset-difference
1096 equal-entry? (list-entries new) (list-entries old)))
1097 (removed (lset-difference
1098 equal-entry? (list-entries old) (list-entries new))))
1099 (for-each (cut display-entry <> "+") added)
88bdbb2a
RJ
1100 (for-each (cut display-entry <> "-") removed)
1101 (newline)))
e95ae7c2
RJ
1102
1103 (display-diff profile gen1 gen2))
1104
ad18c7e6
LC
1105(define (display-profile-content profile number)
1106 "Display the packages in PROFILE, generation NUMBER, in a human-readable
1107way."
1108 (for-each (match-lambda
1109 (($ <manifest-entry> name version output location _)
1110 (format #t " ~a\t~a\t~a\t~a~%"
1111 name version output location)))
1112
1113 ;; Show most recently installed packages last.
1114 (reverse
1115 (manifest-entries
1116 (profile-manifest (generation-file-name profile number))))))
1117
06d45f45
LC
1118(define (display-generation-change previous current)
1119 (format #t (_ "switched from generation ~a to ~a~%") previous current))
1120
1121(define (roll-back* store profile)
1122 "Like 'roll-back', but display what is happening."
1123 (call-with-values
1124 (lambda ()
1125 (roll-back store profile))
1126 display-generation-change))
1127
1128(define (switch-to-generation* profile number)
1129 "Like 'switch-generation', but display what is happening."
1130 (let ((previous (switch-to-generation profile number)))
1131 (display-generation-change previous number)))
1132
1133(define (delete-generation* store profile generation)
1134 "Like 'delete-generation', but display what is going on."
1135 (format #t (_ "deleting ~a~%")
1136 (generation-file-name profile generation))
1137 (delete-generation store profile generation))
1138
2876b989
LC
1139(define* (package-specification->name+version+output spec
1140 #:optional (output "out"))
1141 "Parse package specification SPEC and return three value: the specified
1142package name, version number (or #f), and output name (or OUTPUT). SPEC may
1143optionally contain a version number and an output name, as in these examples:
1144
1145 guile
1b846da8 1146 guile@2.0.9
2876b989 1147 guile:debug
1b846da8 1148 guile@2.0.9:debug
2876b989
LC
1149"
1150 (let*-values (((name sub-drv)
1151 (match (string-rindex spec #\:)
1152 (#f (values spec output))
1153 (colon (values (substring spec 0 colon)
1154 (substring spec (+ 1 colon))))))
1155 ((name version)
1156 (package-name->name+version name)))
1157 (values name version sub-drv)))
1158
1159\f
1160;;;
1161;;; Command-line option processing.
1162;;;
1163
e49951eb 1164(define (show-guix-usage)
e49951eb 1165 (format (current-error-port)
25c93676
LC
1166 (_ "Try `guix --help' for more information.~%"))
1167 (exit 1))
e49951eb 1168
e31ff8b8
LC
1169(define (command-files)
1170 "Return the list of source files that define Guix sub-commands."
1171 (define directory
1172 (and=> (search-path %load-path "guix.scm")
1173 (compose (cut string-append <> "/guix/scripts")
1174 dirname)))
1175
2b8cf44f
LC
1176 (define dot-scm?
1177 (cut string-suffix? ".scm" <>))
1178
e31ff8b8 1179 (if directory
09d809db 1180 (scandir directory dot-scm?)
e31ff8b8
LC
1181 '()))
1182
1183(define (commands)
1184 "Return the list of Guix command names."
1185 (map (compose (cut string-drop-right <> 4)
1186 basename)
1187 (command-files)))
1188
1189(define (show-guix-help)
59f734f3 1190 (define (internal? command)
94d92c77
LC
1191 (member command '("substitute" "authenticate" "offload"
1192 "perform-download")))
59f734f3 1193
e31ff8b8
LC
1194 (format #t (_ "Usage: guix COMMAND ARGS...
1195Run COMMAND with ARGS.\n"))
1196 (newline)
1197 (format #t (_ "COMMAND must be one of the sub-commands listed below:\n"))
1198 (newline)
1199 ;; TODO: Display a synopsis of each command.
59f734f3
LC
1200 (format #t "~{ ~a~%~}" (sort (remove internal? (commands))
1201 string<?))
e31ff8b8
LC
1202 (show-bug-report-information))
1203
a2011be5
LC
1204(define program-name
1205 ;; Name of the command-line program currently executing, or #f.
1206 (make-parameter #f))
1207
ec5d0a85
LC
1208(define (run-guix-command command . args)
1209 "Run COMMAND with the given ARGS. Report an error when COMMAND is not
1210found."
1211 (define module
1212 (catch 'misc-error
1213 (lambda ()
1214 (resolve-interface `(guix scripts ,command)))
1215 (lambda -
25c93676
LC
1216 (format (current-error-port)
1217 (_ "guix: ~a: command not found~%") command)
1218 (show-guix-usage))))
ec5d0a85
LC
1219
1220 (let ((command-main (module-ref module
1221 (symbol-append 'guix- command))))
1222 (parameterize ((program-name command))
14d5ca2e
LC
1223 ;; Disable canonicalization so we don't don't stat unreasonably.
1224 (with-fluids ((%file-port-name-canonicalization #f))
1225 (apply command-main args)))))
ec5d0a85 1226
caa6732e
AK
1227(define (run-guix . args)
1228 "Run the 'guix' command defined by command line ARGS.
1229Unlike 'guix-main', this procedure assumes that locale, i18n support,
1230and signal handling has already been set up."
1231 (define option? (cut string-prefix? "-" <>))
1232
1233 (match args
1234 (()
1235 (format (current-error-port)
1236 (_ "guix: missing command name~%"))
1237 (show-guix-usage))
1238 ((or ("-h") ("--help"))
1239 (show-guix-help))
1240 (("--version")
1241 (show-version-and-exit "guix"))
1242 (((? option? o) args ...)
1243 (format (current-error-port)
1244 (_ "guix: unrecognized option '~a'~%") o)
1245 (show-guix-usage))
2ab5fdc4
LC
1246 (("help" command)
1247 (apply run-guix-command (string->symbol command)
1248 '("--help")))
caa6732e
AK
1249 (("help" args ...)
1250 (show-guix-help))
1251 ((command args ...)
1252 (apply run-guix-command
1253 (string->symbol command)
1254 args))))
1255
a2011be5
LC
1256(define guix-warning-port
1257 (make-parameter (current-warning-port)))
1258
e49951eb
MW
1259(define (guix-main arg0 . args)
1260 (initialize-guix)
caa6732e 1261 (apply run-guix args))
e49951eb 1262
073c34d7 1263;;; ui.scm ends here