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