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